一、什么是装饰器?
装饰器本质上就是一个Python函数,它可以让其它函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景:比如插入日志,性能测试,事物处理,缓存等等场景。
二、装饰器的形成过程
现在我有一个需求,我想让你在不改变函数代码的情况下,测试出这个函数的执行时间:
import time def func1(): print("in func1") def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner func1 = timer(func1) func1()
但是如果有多个函数,我都想让你测试他们的执行时间,你岂不是每个函数都要写一遍func1 = timer(func1)?
这样写非常麻烦,因为这些函数的函数名都是不相同的,有func1、func2,func3等等。
针对这种情况,python给我们提供了一个简单快捷的方法,那就是语法糖。
1 import time 2 3 4 def timer(func): 5 def inner(): 6 start = time.time() 7 func() 8 print(time.time() - start) 9 return inner 10 11 12 @timer # ==> func1 = timer(func1) 13 def func1(): 14 print("in func1") 15 16 17 func1()
刚刚我们讨论的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数要怎么办呢?
1 import time 2 3 def timer(func): 4 def inner(a): 5 start = time.time() 6 func(a) 7 print(time.time() - start) 8 return inner 9 10 11 @timer 12 def func1(a): 13 print(a) 14 15 func1(1)
1 import time 2 3 4 def timer(func): 5 def inner(*args, **kwargs): 6 start = time.time() 7 result = func(*args, **kwargs) 8 print(time.time() - start) 9 return result 10 return inner 11 12 13 @timer #==> func1 = timer(func1) 14 def func1(a, b): 15 print("in func1") 16 17 18 @timer #==> func2 = timer(func2) 19 def func2(a): 20 print("in func2 and get a:%s" % a) 21 return "func2 end" 22 23 24 func1("aaa", "bbb") 25 print(func2("aa"))
上面的装饰器已经非常完美了,但是我们在正常情况下查看函数信息的方法却在此处全部失效了:
def index(): """ 这是一个主页信息 :return: """ print("from index") print(index.__doc__) # 查看函数注释的方法 print(index.__name__) # 查看函数名的方法
那么如何解决这个问题呢?
from functools import wraps def deco(func): @wraps(func) # 放在最内层函数最上方 def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @deco def index(): """ 显示首页信息 :return: """ print("from index") print(index.__doc__) print(index.__name__)
三、开放封闭原则
装饰器是完美的遵循了这个开放封闭原则的。
那么什么是开放封闭原则呢?
我们可以从下面两个方面来看。
1. 对扩展是开放的
为什么要对扩展开放呢?
我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能,并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
2. 对修改是封闭的
为什么要对修改封闭呢?
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。
四、装饰器的主要功能和固定结构
def timer(func): def inner(*args, **kwargs): """执行函数之前要做的""" result = func(*args, **kwargs) """执行函数之后要做的""" return result return inner
1 from functools import wraps 2 3 def deco(func): 4 @wraps(func) # 加在最内层函数最上方 5 def wrapper(*args, **kwargs): 6 return func(*args, **kwargs) 7 return wrapper
五、带参数的装饰器
假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?
如果一个一个的取消,那任务量也太大了吧。
万一,没过几天,你又需要用这些装饰器,岂不是要吐血。
那么解决办法,就是在装饰器上加上参数:
1 def wrapper_out(flag): 2 def wrapper(func): 3 @wraps(func) 4 def inner(*args, **kwargs): 5 if flag: 6 print("执行函数之前要做的") 7 8 result = func(*args, **kwargs) 9 10 if flag: 11 print("执行函数之后要做的") 12 13 return result 14 return inner 15 return wrapper 16 17 18 @wrapper_out(False) # 通过传递True和False来控制装饰器内部的运行效果 19 def func(): 20 pass 21 22 23 func()
六、多个装饰器装饰一个函数
先执行下面这样一个代码
1 def wrapper1(func): 2 def inner(*args, **kwargs): 3 print("111") 4 result = func(*args, **kwargs) 5 print("222") 6 return result 7 return inner 8 9 10 def wrapper2(func): 11 def inner(*args, **kwargs): 12 print("333") 13 result = func(*args, **kwargs) 14 print("444") 15 return result 16 return inner 17 18 19 @wrapper2 20 @wrapper1 21 def func(): 22 print("this is func") 23 24 func()
当执行完毕后,可以看到执行结果为:
333 111 this is func 222 444
执行顺序:首先@warpper1装饰器来,然后获取到一个新函数是wrapper1中的inner,然后执行@wrapper2。这个时候,wrapper2装饰的就是wrapper1中的inner了。
所以,执行顺序就像:第二层装饰器前(第一层装饰器前(目标)第一层装饰器后)第二层装饰器后。程序从左到右执行起来,这就是我们看到的结果。