装饰器
1、一个普通的装饰器
装饰器可以在不改变原有函数基础上增加功能
下面定义了一个增加输出函数运行时间的装饰器
import time from functools import wraps def timethis(func): @wraps(func) def warpper(*args,**kwargs): start = time.time() result = func(*args,**kwargs) end = time.time() print(func.__name__,end - start,sep=':') return result return warpper @timethis def test(n:int): ''' :param n: :return: ''' while n>0: n-=1 #@timethis等价于test = timethis(test) test(1000000) print(test.__name__)#函数名 print(test.__doc__)#注释 print(test.__annotations__)#形参类型注释
@wraps(func)可以保存原函数中的信息
@wraps(func)可以通过__wrapped__属性访问被包装的函数
(原始函数)test(n)等价于test.__wrapped__(n)
目前看来@wraps(func)好处多多,百利无害,是否可以作为标准写法无脑加呢??
2、可以接受参数的装饰器
在上面装饰器的基础上做的修改,在原本的timethis外面再套一层来接受参数
在原来的输出函数运行时间的基础上延迟t秒
import time from functools import wraps def timedelay(t=0): def timethis(func): @wraps(func) def warpper(*args,**kwargs): start = time.time() result = func(*args,**kwargs) end = time.time() print(func.__name__,end - start+int(t),sep=':') return result return warpper return timethis @timedelay(3) def test(n:int): ''' test函数 ''' while n>0: n-=1 return 'end' test(1000000) print(test.__name__)#函数名 print(test.__doc__)#注释 print(test.__annotations__)#形参类型注释 print(test.__wrapped__(10000))
@timedelay(3)等价于test = timedelay(3)(test)
3、用类的方式定义装饰器
用类的方式定义装饰器,可以得到一个可调用的实例,进而可以使用实例中的数据
下面的装饰器增加了记录函数调用次数的功能
import types from functools import wraps class Profield: def __init__(self,func): wraps(func)(self) self.ncalls = 0 def __call__(self, *args, **kwargs): self.ncalls += 1 return self.__wrapped__(*args, **kwargs) def __get__(self, instance, owner): if instance is None: return self else: return types.MethodType(self,instance) @Profield def add(x,y): return x+y print(add(1,2)) print(add(1,2)) print(add.ncalls) class Test: @Profield def bar(self,x): print(x) t = Test() t.bar(1) t.bar(2) t.bar(3) print(Test.bar.ncalls)
@Profield等价于add=Profield(add)
wraps函数可以使用__wraps__来使用原始函数,从而不影响原始函数的基础上添加新的功能
4、在类中使用自定义装饰器
需要注意自定义装饰器要写在类/静态方法装饰器得里面
import types,time from functools import wraps def timedelay(t=0): def timethis(func): @wraps(func) def warpper(*args,**kwargs): start = time.time() result = func(*args,**kwargs) end = time.time() print(func.__name__,end - start+int(t),sep=':') return result return warpper return timethis class Test: @staticmethod @timedelay(1) def do(n:int): sum = 0 for i in range(n): sum+=i Test.do(100000)
5、装饰器执行顺序
多个装饰器执行按照正常程序流程,自上向下。装饰器封装按照从内到外,自下而上。
所以得出结论,为了确保某些你不知道实现的装饰器正确运行,要将其写在最外面,最后再做封装,以确保不改变其功能。
例如类装饰器@staticmethod,Flask中的路由装饰器@app.route('/'),都要写在最外面。
def test1(func): def warpper(*args,**kwargs): return "1111"+func(*args,**kwargs)+"1111" return warpper def test2(func): def warpper(*args,**kwargs): return "2222"+func(*args,**kwargs)+"2222" return warpper @test1 @test2 def func(): return 'haha' print(func()) 执行结果: 11112222haha22221111 可以看出先封装test2 先执行的test1