要理解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