Python装饰器
装饰器介绍
啥是装饰器呀?
答:给函数对象增加新功能的一个工具。装饰器的内部函数其实就是一个闭包函数。
装饰器有啥原则呀?
答:利用装饰器增加新功能时,不修改被装饰对象的源代码,不改变被装饰对象的调用方式。
简单装饰器示例
有一个函数: def func(): print('我是一个函数') 给这个函数加个功能,计算该函数运行时间。给一个函数加功能,不改变函数源代码,不改变函数调用方式,使用装饰器! import time def count_time(func): def wrapper(): start = time.time() time.sleep(2) func() print('函数执行消耗的时间为:%s' % (time.time()-start)) return wrapper func = count_time(func) #本质上是返回wrapper的函数。 func() #本质上调用的是 wrapper(), wrapper里面的func()才能调用的原func函数。
装饰器语法糖:为了直观,python 为了省略 func = count_time(func)这一步,提出了语法糖的概念。
import time def count_time(func): def wrapper(): start = time.time() time.sleep(2) func() print('函数执行消耗的时间为:%s' % (time.time()-start)) return wrapper @count_time #重点:这句话等同于: func = count_time(func) def func(): print('我是一个函数') func() #本质上调用的还是count_time(func)返回的 wrapper(), wrapper里面的func()才能调用的原func函数。
当被装饰器有参数和返回值的时候该怎么办?
这里介绍一下牛逼的装饰器模板,直接用就完事了。推导过程就不写了,累了,等我想起来了再说吧...(ps: 虽然模板是抄的,但程序员嘛,程序员抄东西,能叫抄吗?不能,这叫借鉴。)
def outter(func): def wrapper(*args,**kwargs): print('执行被装饰函数之前 你可以做的操作') res = func(*args,**kwargs) print('执行被装饰函数之后 你可以做的操作') return res return inner @outter # 恒等于 func = outter(func) 返回wrapper这个闭包函数。 def func(a, b, c, d, e, f) print(a, b, c, d, e, f) return '我是一个帅比' #这里调用的时候 func(1,2,3,4,5,6) #就相当于 调用wrapper(1, 2, 3, 4, 5, 6)
多层装饰器
装饰器在装饰的时候 顺序从下往上。如👇所示:装饰的时候先调用 outtter3(), 再outtter2(), 在outtter3()
装饰器在执行的时候 顺序从上往下。
那么,装饰器什么时候执行呢?就是被装饰函数被调用的时候。如下,调用index,index(), 然后执行多层装饰器时,会先调用最外面的,也就是 outter1(), outter2(), 最后outter3()
def outter1(func1): print('加载了outter1') def wrapper1(*args,**kwargs): print('执行了wrapper1') res1=func1(*args,**kwargs) return res1 return wrapper1 def outter2(func2): print('加载了outter2') def wrapper2(*args,**kwargs): print('执行了wrapper2') res2=func2(*args,**kwargs) return res2 return wrapper2 def outter3(func3): print('加载了outter3') def wrapper3(*args,**kwargs): print('执行了wrapper3') res3=func3(*args,**kwargs) return res3 return wrapper3
@outter1 # index = outter1(wapper2) @outter2 # wrapper2 = outter2(wrapper3) @outter3 # wrapper3 = outter3(最原始的index函数内存地址) def index(): print('from index') index() >>>加载了outter3 >>>加载了outter2 >>>加载了outter1 >>>执行了wrapper1 >>>执行了wrapper2 >>>执行了wrapper3 >>>from index
再来一个例子:
def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f() >>>wrapper2 ,before func >>>wrapper1 ,before func >>>in f >>>wrapper1 ,after func >>>wrapper2 ,after func
#注意,这个例子的wrapper,只有被装饰器执行调用的时候才被调用。
装饰器传参
当想给装饰器传个参数的时候,我们知道,传参数的方式有两种:1、普通函数传参。2、闭包传参。
如果我们给装饰器后面写个括号,传参数,装饰器不就被调用了,况且装饰器里面的参数只能有一个,那就是被装饰的那个函数。
所以这时候,我们选择闭包传参。怎么闭包传参呢?
在装饰器外面再套一层函数不就OK了。如下所示:
def outer_two(a, b, c): def outer(func): def wrapper(*args, **kwargs): if a == 1: res = func(*args, **kwargs) return res else: print('如果a不等于1执行的操作') return wrapper return outer @outer_two(1, 2, 3) #这个就等于 outer, 因为outer_tow调用时的返回值时outer def func(): print('被装饰器')
装饰器补充点
由来:使用装饰器有一个小毛病,就是想要查看被装饰器的注释或者名字时,比如使用如下方法时:
print(help(index)) # 查看函数的注释
print(index.__name__) # 查看函数名字符串形式
结果看到的注释不是被装饰器的注释,而是装饰器里面的内部闭包函数的注释。
查看到的函数名字也是装饰器内部函数的名字,这样我就不爽了,我调用函数的时候,想看函数的名字和注释,看到的却不是对的。
基于以上情况,Python提供了解决方案,叫做装饰器修复技术。
from functools import wraps def outter(func): @wraps(func) # 装饰器修复技术 def inner(*args,**kwargs): """ 我是inner函数 :param args: :param kwargs: :return: """ print('执行被装饰函数之前 你可以执行的操作') res = func(*args,**kwargs) print('执行被装饰函数之后 你可以执行的操作') return res return inner @outter # index = outter(最原始的index内存地址) def index(): """ 这是index函数 :return: """ pass #注意wraps使用的位置!
这时候:
用户查看被装饰函数的函数名的时候查看到的就是被装饰函数本身
用户查看被装饰函数的注释的时候查看到的就是被装饰函数的注释