要理解Python装饰器,首先要明白在Python中,函数也是一种对象,所以能够把定义函数时的函数名看做是函数对象的一个引用。既然是引用,所以能够将函数赋值给一个变量,也能够把函数做为一个参数传递或返回。同时,函数体中也能够再定义函数。python
能够经过编写一个纯函数的例子来还原装饰器所要作的事。函数
def decorator(func): def wrap(): print("Doing someting before executing func()") func() print("Doing someting after executing func()") return wrap def fun_test(): print("func") fun_test = decorator(fun_test) fun_test() # Output: # Doing someting before executing func() # func # Doing someting after executing func()
fun_test
所指向的函数的引用传递给decorator()
函数decorator()
函数中定义了wrap()
子函数,这个子函数会调用经过func
引用传递进来的fun_test()
函数,并在调用函数的先后作了一些其余的事情decorator()
函数返回内部定义的wrap()
函数引用fun_test
接收decorator()
返回的函数引用,从而指向了一个新的函数对象fun_test()
调用新的函数执行wrap()
函数的功能,从而完成了对fun_test()
函数的先后装饰在Python中能够经过@
符号来方便的使用装饰器功能。code
def decorator(func): def wrap(): print("Doing someting before executing func()") func() print("Doing someting after executing func()") return wrap @decorator def fun_test(): print("func") fun_test() # Output: # Doing someting before executing func() # func # Doing someting after executing func()
装饰的功能已经实现了,可是此时执行:对象
print(fun_test.__name__) # Output: # wrap
fun_test.__name__
已经变成了wrap
,这是应为wrap()
函数已经重写了咱们函数的名字和注释文档。此时能够经过functools.wraps
来解决这个问题。wraps
接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等功能。这能够让咱们在装饰器里面访问在装饰以前的函数的属性。blog
更规范的写法:继承
from functools import wraps def decorator(func): @wraps(func) def wrap(): print("Doing someting before executing func()") func() print("Doing someting after executing func()") return wrap @decorator def fun_test(): print("func") fun_test() print(fun_test.__name__) # Output: # Doing someting before executing func() # func # Doing someting after executing func() # fun_test
经过返回一个包裹函数的函数,能够模仿wraps装饰器,构造出一个带参数的装饰器。文档
from functools import wraps def loginfo(info='info1'): def loginfo_decorator(func): @wraps(func) def wrap_func(*args, **kwargs): print(func.__name__ + ' was called') print('info: %s' % info) return func(*args, **kwargs) return wrap_func return loginfo_decorator @loginfo() def func1(): pass func1() # Output: # func1 was called # info: info1 @loginfo(info='info2') def func2(): pass func2() # Output: # func2 was called # info: info2
经过编写类的方法也能够实现装饰器,并让装饰器具有继承等面向对象中更实用的特性it
首先编写一个装饰器基类:class
from functools import wraps class loginfo: def __init__(self, info='info1'): self.info = info def __call__(self, func): @wrap def wrap_func(*args, **kwargs): print(func.__name__ + ' was called') print('info: %s' % self.info) self.after() # 调用after方法,能够在子类中实现 return func(*args, **kwargs) return wrap_func def after(self): pass @loginfo(info='info2') def func1(): pass # Output: # func1 was called # info: info1
再经过继承loginfo
类,扩展装饰器的功能:test
class loginfo_after(loginfo): def __init__(self, info2='info2', *args, **kwargs): self.info2 = info2 super(loginfo_after, self).__init__(*args, **kwargs) def after(self): print('after: %s' % self.info2) @loginfo_after() def func2(): pass func2() # Output: # func2 was called # info: info1 # after: info2