装饰器本质上就是一个Python函数,它可让其它函数在不须要作任何代码变更的前提下,增长额外的功能,装饰器的返回值也是一个函数对象。python
装饰器的应用场景:好比插入日志,性能测试,事物处理,缓存等等场景。缓存
如今我有一个需求,我想让你在不改变函数代码的状况下,测试出这个函数的执行时间:app
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)?ide
这样写很是麻烦,由于这些函数的函数名都是不相同的,有func一、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"))
上面的装饰器已经很是完美了,可是咱们在正常状况下查看函数信息的方法却在此处所有失效了:this
def index(): """ 这是一个主页信息 :return: """ print("from index") print(index.__doc__) # 查看函数注释的方法 print(index.__name__) # 查看函数名的方法
那么如何解决这个问题呢?spa
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__)
装饰器是完美的遵循了这个开放封闭原则的。设计
那么什么是开放封闭原则呢?
咱们能够从下面两个方面来看。
为何要对扩展开放呢?
咱们说,任何一个程序,不可能在设计之初就已经想好了全部的功能,而且将来不作任何更新和修改。因此咱们必须容许代码扩展、添加新功能。
为何要对修改封闭呢?
就像咱们刚刚提到的,由于咱们写的一个函数,颇有可能已经交付给其余人使用了,若是这个咱们对其进行了修改,颇有可能影响其余已经在使用该函数的用户。
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了。
因此,执行顺序就像:第二层装饰器前(第一层装饰器前(目标)第一层装饰器后)第二层装饰器后。程序从左到右执行起来,这就是咱们看到的结果。