python基础5之装饰器
内容概要:
| 一、装饰器前期知识储备 |
1.python解释函数代码过程:
python解释器从上往下顺序解释代码,碰到函数的定义代码块不会立即执行它,而是将其放在内存中,等到该函数被调用时,才执行其内部的代码块。
2.函数即“变量”:
函数的使用分为,函数的定义和函数的调用,调用方式是为函数名后加括号(函数名存放的是内存地址)类似于变量的使用(先定义,后使用)。可以用一个很形象的列子比喻,函数体相当于一个房间里的工具,函数名相当于门牌号(内存地址)。

示列:
#/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:W-D
def test():
print("this is test")
acl=test#变量赋值,内存地址赋值
print(acl,test)#打印函数的内存地址
acl()#调用,相当于调用test()
结果:
<function test at 0x0000000000B0E048> <function test at 0x0000000000B0E048>
this is test
3.变量的回收机制:
前面说过函数即变量,当变量的引用阶数为0的时候,其变量的内存才会被回收,并且我们使用del删除的时候只是删除了变量的指向地址,实际变量的值的内存是python定时回收的。tips:变量的阶就是变量被引用的次数,例如x=1,阶就是1,若此时y=x,阶数就是2.
#报错代码
#/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:W-D
def test():
print("this is test")
bar()#未定义就先进行了调用,会报错
def bar():
print("this is bar")
test()
######未报错代码
#/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:W-D
def test():
print("this is test")
bar()
def bar():
print("this is bar")
test()#当解释器运行到这里时候,bar函数已经经过解释器解释过了,存在了内存中,此时test函数中再调用bar不会报错。
结果:
this is test
this is bar
4.函数的嵌套:
函数的嵌套就是在一个函数中定义一个或者多个函数,并且嵌套的函数只能在内层调用,函数外部不能直接调用。
示列:
#/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:W-D
def outer():
print("this is outer")
def inner():
print("this is inner")
outer()
结果:
this is outer#结果表明只执行了外层函数的代码,因为内层没有调用 #/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:W-D
def outer():
print("this is outer")
def inner():
print("this is inner")
inner()#内部调用
outer()
结果:
this is outer
this is inner#执行了内层函数代码
5.高阶函数
满足两个规则之一:
a.把一个函数名当作实参传递给另外一个函数;
b.返回值中包含函数名;
示例:
#规则1
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:W-D
def test(fun):#传递函数名
fun()
print("in the test")
def bar():
print("in the bar")
test(bar)
结果:
in the bar
in the test 规则2:
def test(fun):
fun()
print("in the test")
return fun#返回fun函数的内存地址
def bar():
print("in the bar")
a=test(bar)
print(a)
a()#调用返回的函数
结果:
in the bar
in the test
<function bar at 0x000000000104F1E0>
in the bar
6.装饰器
定义:本质是函数,作用给其他函数添加附加功能
原则:a.不能修改被装饰函数的源代码
b.不能修改被装饰函数的调用方式
实现装饰器的步骤:高阶函数+嵌套函数-->装饰器
装饰器使用场景:
公司的网站以前是没有认证功能的,现在产品经理需要你在除了首页以外的地方都加上认证功能,要求不能修改源代码。
假如公司的网站网站如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:W-D
def person():
print("个人板块")
def pub():
print("公共板块")
def index():
print("首页")
index()
pub()
person(
装饰器解决方案:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:W-D
Username="wd"
passwd="123qwe"
def auth(fun):
def deco():#装饰函数
username=input("plese input your username>>:")
password=input("plese input your password>>:")
if username==Username and password==passwd:#认证功能
fun()
else:
exit("invaild username or passwd!")
return deco#返回装饰函数的内存地址 @auth#添加装饰器
def person():
print("个人板块")
def pub():
print("公共板块") def index():
print("首页")
index()
pub()
person()
结果:
首页
公共板块
plese input your username>>:wd
plese input your password>>:123qwe
个人板块#输入了用户名密码以后才能看见个人模块
实现过程:
1.正如上面所说,装饰器的实现需要高阶函数+嵌套函数,那么我们一步一步来看怎么样实现的:
第一步:满足要求一,利用return不改变其源代码的调用方式,把封闭的内层函数地址返回出来,可以供外部调用。
def deco(fun):
username=input("plese input your username>>:")
password=input("plese input your password>>:")
if username==Username and password==passwd:
fun()
else:
exit("invaild username or passwd!")
return deco#返回deco的内存地址
def person():
print("个人板块")
person=deco(person)#将返回的地址赋值给person
person()#调用person相当于调用deco,为修改其调用方式,但是person已经被偷梁换柱了
结果:
plese input your username>>:wd
plese input your password>>:ada
invaild username or passwd!
第二步:满足要求二,利用嵌套函数。
def auth(fun):
def deco():#定义了一个内层函数,相当于“变量”
username=input("plese input your username>>:")
password=input("plese input your password>>:")
if username==Username and password==passwd:
fun()
else:
exit("invaild username or passwd!")
return deco#返回deco内存地址
def person():
print("个人板块")
person=auth(person)#将返回的deco内存地址复制给person
person()#此时调用person相当于调用deco,未改变调用方式,但是此时的person已经不是原来的person。
结果:
plese input your username>>:wd
plese input your password>>:123qwe
个人板块
第三步:关于语法糖@
python中有很多优化语法,例如在我前面代码中使用的@,相当与在这里执行了person=auth(person),此时相当于调用了已经调用了auth函数,将deco函数对象返回给person。
这个语法是死的,这里的语法为:被装饰的函数的名字会被当作参数传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰的函数。
def auth(fun):
print("in the auth")
def deco():
username=input("plese input your username>>:")
password=input("plese input your password>>:")
if username==Username and password==passwd:
fun()
else:
exit("invaild username or passwd!")
return deco
@auth#相当与执行person=auth(person)这里已经调用了auth函数,目的将deco函数偷梁换柱成person
def person():
print("个人板块")
person()
结果:
in the auth#该结果在调用person前面就会出现,验证时候注释掉person()即可验证
plese input your username>>:wd
plese input your password>>:123qwe
个人板块
| 二、装饰器 |
前面已经实现了装饰器,但是被装饰的函数一般来说都有自己的参数,所以来看下一般的装饰器:
def auth(fun):
def deco(*args,**kwargs):#传递参数
username=input("plese input your username>>:")
password=input("plese input your password>>:")
if username==Username and password==passwd:
fun(*args,**kwargs)
else:
exit("invaild username or passwd!")
return deco
@auth
def person(name):
print("%s的个人板块"%name)
person("wd")
结果:
plese input your username>>:wd
plese input your password>>:123qwe
wd的个人板块
装饰器内部原理:
内部原理我们可以从上面的代码解释说明:
1.python解释器从上往下解释运行代码,到读到def auth(fun)的时候,解释器发现它是一个函数,于是将函数体加载在内存中,然后跳过到达@auth
2.解释器读取@auth发现是个装饰函数,此时相当于解释到person=auth(person),此时已经调用函数auth,并且执行函数体,发现def deco(*args,**kwargs)又是一个函数,于是将函数体加载到内存,接着跳到return deco,此时将deco函数的内存地址赋值给你person,经过这个步骤,person变量的内存地址指向了deco,下面调用person相当于调用deco。
3.解释器解释到person(),此时的person的内存地址是指向deco的,调用person就是调用deco,然后执行deco的函数体,执行deco的函数体就加了认证功能,并且在执行此时person,这样就完美的把需求实现了。
有些人可能会问,为什么需要在外部包装一层函数?
答案是:如果不再外部包装一层装饰函数,当我们使用装饰器时直接就执行了该函数,此时我们都还没有调用,然后程序已经执行了,这显然不是我们所要的。
例子:
def logger(fun):
print("日志功能")
fun()
return logger
@logger
def test():
print("in the test")
结果:#还未调用test,logger函数就执行了
日志功能
in the test
| 三、装饰器高潮版 |
当被装饰的函数有返回值时,这时候我们该怎么处理呢,请看下面的表演。
先观察下面例子,如果按照原来的方法,代码执行完,被修饰函数的返回值丢了;
def logger(fun):
def deco():
print("添加日志功能")
fun()
return deco
@logger
def person():
print("欢迎来到个人专区")
return "hello"
a=person()
print(a)
结果:
添加日志功能
欢迎来到个人专区
None
#可以看到person的返回值应该是hello,但是这里变成了None,这是为什么呢?因为这里person相当执行deco,此时得到的返回值是deco的返回值,而deco函数没有返回值,所以a的值为None
那么怎么样才能接收到这个返回值呢,很容易,使用变量接收fun的返回值,并将其在deco函数中return出来,请看下面代码;
def logger(fun):
def deco():
print("添加日志功能")
res=fun()#接收被装饰函数的返回值
return res#将结果返回出来
return deco
@logger
def person():
print("欢迎来到个人专区")
return "hello"
a=person()
print(a)
结果:
添加日志功能
欢迎来到个人专区
hello
| 四、装饰器之最终高潮版--带参数的装饰器 |
有人可能会问,既然装饰器本质上也是函数,那它可以传递参数吗?当然可以,请继续看下面的表演;
username="wd"
passwd="123qwe"
def logger(type):#装饰器传递的参数在这里
def auth(fun):#这里传递被装饰的函数作为参数
def deco(*args,**kwargs):#原来的装饰函数
print("添加日志功能")
if type=="local":
_username=input("please input your username>>")
_passwd = input("please input your username>>")
if _username==username and _passwd==passwd:
print("本地认证成功!")
res = fun(*args,**kwargs)
return res
#在这里返回res,其他地方可能res这个变量不存在导致报错
else:
print("认证失败!")
elif type=="remote":
print("远程认证成功!")
else:
print("认证失败,再见!")
return deco
return auth
@logger(type="local")
def person():
print("欢迎来到个人专区")
return "hello"
a=person()
print(a)
结果:
添加日志功能
please input your username>>wd
please input your username>>123qwe
本地认证成功!
欢迎来到个人专区
hello
解释说明:装饰器带参数,原理是在原来的装饰函数基础之上又封装了一层函数,这层函数作用是为了接收参数(当然,参数可以是函数),用于后面的处理。
python基础5之装饰器的更多相关文章
- python基础-内置装饰器classmethod和staticmethod
面向对象编程之classmethod和staticmethod classmethod 和 staticmethod都是python内置的装饰器 classmethod 的作用:给在类内部定义的方法装 ...
- python基础篇_004_装饰器函数
python装饰器函数 1.装饰器函数引导 功能:计算函数执行时长 import time """ 方式一: 函数首位添加时间,差值就是函数执行时间 缺点:每个函数都要加 ...
- python基础-面向对象(装饰器)
属性: @property @method_name.setter @method_name.deleter 三个标签都是放在方法的上面来使用,且方法名要和后续使用的 变量名字相一 ...
- python基础-5.2装饰器
1.了解装饰器前准备 #### 第一波 #### def foo(): print 'foo' foo #表示是函数,仅指向了函数的地址,为执行 foo() #表示执行foo函数 #### 第二波 # ...
- python基础之内置装饰器
装饰器 简介 功能与格式 内置装饰器 @classmethod @propertry @staticmethod 其它 ---------------------------------------- ...
- 【笔记】Python基础五:装饰器
一,什么是装饰器 本质就是函数,功能是为其他函数添加附加功能 原则: 1,不修改被修饰函数的源代码 2,不修改被修饰函数的调用方式 例子: import time def timmer(func): ...
- python 基础篇 12 装饰器进阶
本节主要内容:1. 通⽤装饰器回顾2. 函数的有⽤信息3. 带参数的装饰器4. 多个装饰器同时装饰⼀个函数 ⼀. 通⽤装饰器的回顾开闭原则: 对增加功能开放. 对修改代码封闭装饰器的作⽤: 在不改变原 ...
- python基础-函数之装饰器、迭代器与生成器
1. 函数嵌套 1.1 函数嵌套调用 函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数 def bar(): print("from in the bar.") def f ...
- Python基础-迭代器&生成器&装饰器
本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 1.列表生成式,迭代器&生成器 列表生成式 我现在有个需求,看 ...
随机推荐
- Python 实现网页截屏、查库、发邮件
本文介绍了使用 Python(2.7版本)实现网页截屏.查库.发邮件的 demo.用到了 selenium.phantomjs.mailer.jinja2.mysqldb 还有 image,都是比较典 ...
- CheckForIllegalCrossThreadCalls = false
多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,这时如果想要访问窗口的控件,发现无法对其控制. 这时可将窗口构造函数中的CheckForIllegalCrossThreadCalls设置为f ...
- CommonJS, AMD ,CMD之间的关系
commonjs是用在服务器端的,同步的,如nodejs amd, cmd是用在浏览器端的,异步的,如requirejs和seajs 其中,amd先提出,cmd是根据commonjs和amd基础上提出 ...
- window10上登录Oracle时提示ORA-12546:Permission denied
在64位的Windows 10上安装了Oracle 10.2.0.4的64位版,遇到不少问题. 虽然可能现在安装这个版本越来越少,还是分享出来,希望能帮助到一些人. 1.安装的过程遇到的问题 在普通用 ...
- mysql字符串操作相关函数用法总结
功能用法简单例子一览表 函数 功能 用法 例子 left() 从字符串左边为边界返回相应长度的子字符串 left(str, length) mysql> select left('vssf',3 ...
- Watch time
Stopwatch watch = new Stopwatch(); watch.Start(); ; i <= ; i++) { ; j <= ; j++) { ; k <= ; ...
- linux下使用 FreeRDP 连接 Windows 远程桌面(转)
这是一种最简单的使用方法,转载自http://wenzhixin.net.cn/2014/03/16/ubuntu_xfreerdp 简介 FreeRDP 是一款开源的远程桌面系统,支持多种平台, 在 ...
- python 数据类型之集合
一.集合的定义: 定义:由不同元素组成的集合,集合中是一组无序排列的可hash值,可以作为字典的key. 特性:集合的目的是将不同的值存放到一起,不同的集合间用来做关系运算,无需纠结于集合中单个值 二 ...
- Codeforces 714A Meeting of Old Friends
A. Meeting of Old Friends time limit per test:1 second memory limit per test:256 megabytes input:sta ...
- [51nod1684]子集价值
lyk最近在研究位运算. 它发现除了xor,or,and外还有很多运算. 它新定义了一种运算符"#". 具体地,可以由4个参数来表示. ai,j表示 i#j. 其中i,j与a的值均 ...