1、装饰器的定义
装饰器的本质就是函数,用来装饰其它函数,就是为其它函数添加附加功能。
装饰器原则如下:
不能修改被装饰的函数的源代码
不能修改被装饰的函数的调用方式
2、实现装饰器知识储备
函数即变量
1 defbar():2 print("in the bar")3 deffoo():4 print("in the foo")5 bar()6
7 foo()8
9 print("----------分割线-----------")10
11 deffoo1():12 print("in the foo")13 bar1()14 defbar1():15 print("in the bar")16
17 foo1()18
19 print("----------分割线-----------")20 #这样会报错
21 #def foo2():
22 #print("in the foo")
23 #bar2()
24 #foo2()
25 #def bar2():
26 #print("in the bar")
高阶函数
把一个函数名当作实参传递给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
1 importtime2
3 defbar1():4 time.sleep(3)5 print("in the bar")6
7 deftest2(func):8 start_time =time.time()9 func() #相当于运行bar1()
10 stop_time =time.time()11 print("total run time %s" %(stop_time -start_time))12
13 test2(bar1)
返回值中包含函数名(不能修改函数的调用方式)
1 defbar2():2 time.sleep(3)3 print("in the bar2")4
5 deftest3(func):6 print(func)7 returnfunc8
9 print(test3(bar2)) #获取的是内存地址
10
11 res =test3(bar2)12 res()
嵌套函数
1 deffoo():2 print("in the foo")3 defbar():4 print("in the bar")5 bar() #局部变量只能在其作用域内调用
6
7 foo()
1 x =02 defgrandpa():3 x = 1
4 defdad():5 x = 2
6 defson():7 x = 3
8 print(x) #最终打印结果为3
9 son()10 dad()11 grandpa()
高阶函数 + 嵌套函数 --》装饰器
1 importtime2
3
4 deftimer(func):5 defdeco():6 start_time =time.time()7 func()8 stop_time =time.time()9 print("total time is %s" % (stop_time -start_time))10 return deco #返回deco()的内存地址
11
12
13 deftest1():14 time.sleep(3)15 print("in the test1")16
17
18 deftest2():19 time.sleep(3)20 print("in the test2")21
22 #以下可直接用装饰器语法代替
23 timer(test1) #test1的内存地址赋值给func,返回deco()的内存地址
24 print(timer(test1)) #返回deco()的内存地址
25 test1 = timer(test1) #内存地址赋值给test1
26 test1() #相当于执行deco()
27
28 timer(test2)29 test2 =timer(test2)30 test2()31
32 print("---------我是分隔符---------")33
34
35 #装饰器语法如下。(和上面引用的效果一样)
36 @timer #相当于test1 = timer(test1)
37 deftest1():38 time.sleep(3)39 print("in the test1")40
41
42 @timer #相当于test1 = timer(test1)43 deftest2():44 time.sleep(3)45 print("in the test2")46
47
48 test1()49 test2()
3、动态参数装饰器
deftimer(bar):def inner(*args, **kwargs):
start_time=time.time()
foo_ret= bar(*args, **kwargs)
end_time=time.time()
used_time= end_time -start_timeprint(used_time)returnfoo_retreturninner
@timerdef foo(*args, **kwargs):
time.sleep(1)print("我的参数:", args, kwargs)print("我的运行时间:")return "我的返回值"ret= foo("动态参数装饰器", (1, 2), name="Druid", age=18)print(ret)
输出结果如下:
4、装饰器原理图解
4.1 被装饰函数没有返回值
4.2 被装饰函数有返回值
注意:第二步仅为过程分析量,不作为真实的执行顺序。
5、装饰器固定格式
装饰器的固定格式如下例所示:
defwrapper(func):"""该函数为装饰器函数
:param func: 这里的func参数实质是指向被装饰函数的内存地址
:return:"""
def inner(*args, **kwargs):"""该函数为装饰器函数内部函数
:param args: 实质接收的是被装饰函数的位置参数
:param kwargs: 实质接收的是被装饰函数的关键字参数
:return: 返回的是被装饰函数的返回值"""
print("这里放被装饰函数执行之前要做的事")
func_ret= func(*args, **kwargs) #被装饰的函数
print("这里放被装饰函数执行之后要做的事")returnfunc_retreturninner
@wrapper#等价于my_func = wrapper(my_func)
def my_func(*args, **kwargs):"""该函数为被装饰函数
:param args: 接收位置参数
:param kwargs: 接收关键字参数
:return: 返回值"""
print(*args, **kwargs)returnret
ret= my_func() #执行原函数,实质是执行inner()。函数返回值保存在变量ret中。
6、装饰器修复
当我们使用装饰器去装饰某个函数时,我们想要引用被装饰函数原私有属性,如__name__、__doc__时,就有问题了,因为我们虽然仍然在执行被装饰函数,但其实执行的是闭包,看下例。
defwrapper(func):"""该函数为装饰器函数
:param func: 这里的func参数实质是指向被装饰函数的内存地址
:return:"""
def inner(*args, **kwargs):"""该函数为闭包(装饰器函数内部函数)
:param args: 实质接收的是被装饰函数的位置参数
:param kwargs: 实质接收的是被装饰函数的关键字参数
:return: 返回的是被装饰函数的返回值"""func_ret= func(*args, **kwargs) #被装饰的函数
returnfunc_retreturninner
@wrapper#等价于my_func = wrapper(my_func)
def my_func(*args, **kwargs):"""该函数为被装饰函数
:param args: 接收位置参数
:param kwargs: 接收关键字参数
:return: 返回值"""
print(*args, **kwargs)return "返回值"my_func("装饰器没被修复前,被装饰函数原函数的私有属性如__name__、__doc__是获取不到的,如下:") #执行原函数,实质是执行inner()
print(my_func.__name__) #打印函数的名字
print(my_func.__doc__) #打印函数的注释文档
输出结果如下:
如果仍想使用被装饰函数的原私有属性,那么就可以用装饰器修复:
from functools importwrapsdefwrapper(func):"""该函数为装饰器函数
:param func: 这里的func参数实质是指向被装饰函数的内存地址
:return:"""@wraps(func)def inner(*args, **kwargs):"""该函数为闭包(装饰器函数内部函数)
:param args: 实质接收的是被装饰函数的位置参数
:param kwargs: 实质接收的是被装饰函数的关键字参数
:return: 返回的是被装饰函数的返回值"""func_ret= func(*args, **kwargs) #被装饰的函数
print("装饰器修复不会改变原装饰器的作用")returnfunc_retreturninner
@wrapper#等价于my_func = wrapper(my_func)
def my_func(*args, **kwargs):"""该函数为被装饰函数
:param args: 接收位置参数
:param kwargs: 接收关键字参数
:return: 返回值"""
print(*args, **kwargs)return "返回值"my_func("装饰器被修复后,被装饰函数原函数的私有属性如__name__、__doc__就可以正常获取了,如下:") #执行原函数,实质是执行inner()
print(my_func.__name__) #打印函数的名字
print(my_func.__doc__) #打印函数的注释文档
输出结果如下:
7、带参数的装饰器
需求:很多函数共用一个装饰器,要求随时可以关闭装饰器功能,且尽可能的减少代码修改。 该需求可以用标记位来实现,如下:
importtime
FLAG=Truedeftimmer_out(flag):deftimmer(func):def inner(*args, **kwargs):ifflag:
start_time=time.time()
ret_func= func(*args, **kwargs)
end_time=time.time()
used_time= end_time -start_timeprint("函数{name}执行时间:{time}".format(name=func.__name__, time=used_time))#print("函数{name}执行时间:{time}".format_map({"name": func.__name__, "time": used_time}))
else:
ret_func= func(*args, **kwargs)returnret_funcreturninnerreturntimmer
@timmer_out(FLAG)#第一步,先执行timmer_out(FLAG),得到返回值timmer。第二步执行@timmer,即 my_func1 = timmer(my_func1)
defmy_func1():
time.sleep(1)print("my_func1")
@timmer_out(FLAG)defmy_func2():
time.sleep(1)print("my_func2")
@timmer_out(FLAG)defmy_func3():
time.sleep(1)print("my_func3")
my_func1()
my_func2()
my_func3()
当FLAG置为True时,装饰器功能生效,输出结果如下图所示:
当FLAG置为False时,装饰器功能关闭,输出结果如下图所示:
8、多个装饰器装饰一个函数
defwrapper1(func):definner1():print("wrapper1, before func")
func()print("wrapper1, after func")returninner1defwrapper2(func):definner2():print("wrapper2, before func")
func()print("wrapper2, after func")returninner2
@wrapper2
@wrapper1defmy_func():print("function is my func")
my_func()
注意输出结果:
为什么结果是这样?请看如下分析:
为什么是先执行@wrapper1而不是@wrapeer2呢?因为装饰器在找到被装饰函数会优先执行。