函數(shù)也是對(duì)象
要理解Python裝飾器,首先要明白在Python中,函數(shù)也是一種對(duì)象,因此可以把定義函數(shù)時(shí)的函數(shù)名看作是函數(shù)對(duì)象的一個(gè)引用。既然是引用,因此可以將函數(shù)賦值給一個(gè)變量,也可以把函數(shù)作為一個(gè)參數(shù)傳遞或返回。同時(shí),函數(shù)體中也可以再定義函數(shù)。
裝飾器本質(zhì)
可以通過(guò)編寫一個(gè)純函數(shù)的例子來(lái)還原裝飾器所要做的事。
def decorator(func): def wrap(): print("Doing someting before executing func()") func() print("Doing someting after executing func()") return wrapdef 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所指向的函數(shù)的引用傳遞給decorator()函數(shù)
decorator()函數(shù)中定義了wrap()子函數(shù),這個(gè)子函數(shù)會(huì)調(diào)用通過(guò)func引用傳遞進(jìn)來(lái)的fun_test()函數(shù),并在調(diào)用函數(shù)的前后做了一些其他的事情
decorator()函數(shù)返回內(nèi)部定義的wrap()函數(shù)引用
fun_test接收decorator()返回的函數(shù)引用,從而指向了一個(gè)新的函數(shù)對(duì)象
通過(guò)fun_test()調(diào)用新的函數(shù)執(zhí)行wrap()函數(shù)的功能,從而完成了對(duì)fun_test()函數(shù)的前后裝飾
Python中使用裝飾器
在Python中可以通過(guò)@符號(hào)來(lái)方便的使用裝飾器功能。
def decorator(func): def wrap(): print("Doing someting before executing func()") func() print("Doing someting after executing func()") return wrap@decoratordef fun_test(): print("func")fun_test()# Output:# Doing someting before executing func()# func# Doing someting after executing func()裝飾的功能已經(jīng)實(shí)現(xiàn)了,但是此時(shí)執(zhí)行:
print(fun_test.__name__)# Output:# wrap
fun_test.__name__已經(jīng)變成了wrap,這是應(yīng)為wrap()函數(shù)已經(jīng)重寫了我們函數(shù)的名字和注釋文檔。此時(shí)可以通過(guò)functools.wraps來(lái)解決這個(gè)問(wèn)題。wraps接受一個(gè)函數(shù)來(lái)進(jìn)行裝飾,并加入了復(fù)制函數(shù)名稱、注釋文檔、參數(shù)列表等等功能。這可以讓我們?cè)谘b飾器里面訪問(wèn)在裝飾之前的函數(shù)的屬性。
更規(guī)范的寫法:
from functools import wrapsdef decorator(func): @wraps(func) def wrap(): print("Doing someting before executing func()") func() print("Doing someting after executing func()") return wrap@decoratordef fun_test(): print("func")fun_test()print(fun_test.__name__)# Output:# Doing someting before executing func()# func# Doing someting after executing func()# fun_test帶參數(shù)的裝飾器
通過(guò)返回一個(gè)包裹函數(shù)的函數(shù),可以模仿wraps裝飾器,構(gòu)造出一個(gè)帶參數(shù)的裝飾器。
from functools import wrapsdef 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(): passfunc2()# Output:# func2 was called# info: info2裝飾器類
通過(guò)編寫類的方法也可以實(shí)現(xiàn)裝飾器,并讓裝飾器具備繼承等面向?qū)ο笾懈鼘?shí)用的特性
首先編寫一個(gè)裝飾器基類:
from functools import wrapsclass 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() # 調(diào)用after方法,可以在子類中實(shí)現(xiàn) return func(*args, **kwargs) return wrap_func def after(self): pass@loginfo(info='info2')def func1(): pass # Output:# func1 was called# info: info1再通過(guò)繼承l(wèi)oginfo類,擴(kuò)展裝飾器的功能:
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(): passfunc2() # Output:# func2 was called# info: info1# after: info2以上這篇老生常談Python進(jìn)階之裝飾器就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持武林網(wǎng)。
|
新聞熱點(diǎn)
疑難解答
圖片精選