装饰器
Posted xiaoqichaoren
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰器相关的知识,希望对你有一定的参考价值。
定义
本质是函数,用来装饰其他函数(为其他函数添加附加功能)。通俗来讲:高阶函数 + 嵌套函数 = 装饰器
原则
1.不能修改被装饰的函数的源码
2.函数的调用方式也不能被修改
现存在一个函数 foo(),
import time def foo(): time.sleep(1) print(‘in foo‘) foo()
这时,我希望添加一个新功能:记录 foo() 的运行时间。我可以新建一个嵌套函数来计算函数的运行时间
def timmer(fun): start = time.time() fun(*args, **kwargs) end = time.time() print(‘[DEBUG]{}`s running time:{}‘.format(fun.__name__, end - start)) def foo(): time.sleep(1) print(‘in foo‘) timmer(foo)
这样确实可以实现,但是这样会修改 foo() 函数的调用,实际中这样做是不对的。
为了不修改函数的源码以及函数的调用方式,我们就会用到装饰器
最简单的装饰器
import time def timmer(fun): def warpper(*args, **kwargs): start = time.time() fun(*args, **kwargs) end = time.time() print(‘[DEBUG]{}`s running time:{}‘.format(fun.__name__, end - start)) return warpper @timmer # 加上语法糖后,相当于在timmer函数中嵌套了foo函数 def foo(): time.sleep(1) print(‘in foo‘) if __name__ == ‘__main__‘: foo() # 调用方式仍然是 foo()
加上装饰器@timmer后,foo() 等价于 timmer(foo)
带参的装饰器
def witharg(arg): # 在最外层再套一层函数 def decorator(fun): def wrapper(*args, **kwargs): print(‘outer arg: %d‘ % arg) fun() return wrapper return decorator @witharg(10) def foo(): print(‘in foo‘) if __name__ == ‘__main__‘: foo()
类装饰器
相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。此外,类装饰器还可以依靠类内部的 __call__ 方法来“装饰”函数。
class C: def __init__(self, fun): self.fun = fun def __call__(self): # 修改对象状态 self.fun() print(‘in __call__‘) @C # 类装饰器 def bar(): print(‘in bar‘) bar() print(type(bar)) # <class ‘__main__.C‘>
装饰器的顺序
@a @b @c def F(): print(‘in F‘) F() # 等价于a(b(c(F)))
装饰器 @wraps
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的 docstring 、__name__。
def decorator(fun): def wrapper(*args, **kwargs): fun() print(‘in decorator‘) return wrapper @decorator def foo(): print(‘in foo‘) foo() print(foo.__name__) # foo被装饰器取代,__name__ 变成了 wrapper
为了避免这种情况,python中有 functools.wraps 函数。
wraps本身也是个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。
form functools import wraps def decorator(fun): @wraps(fun) def wrapper(*args, **kwargs): fun() print(‘in decorator‘) return wrapper @decorator def foo(): print(‘in foo‘) foo() print(foo.__name__) # foo
以上是关于装饰器的主要内容,如果未能解决你的问题,请参考以下文章