裝飾器是非常Pythonic的工具之一蒜魄,由于騎在函數(shù)定義語句的身上,看起來非常像Java的標(biāo)注场躯,其實(shí)是一種函數(shù)(或者類)的使用方法或稱語法糖谈为。之所以想要總結(jié)以下的內(nèi)容,是因?yàn)闉g覽了很多與類裝飾器
相關(guān)的教程并未說清其含義是用類的方式寫的裝飾器踢关,還是其裝飾器創(chuàng)建的目的是使用在類上伞鲫。那我們開始吧。
我們先來看一個(gè)最簡單的例子:
def fake_decorator(obj):
print(obj)
class Being(object):
pass
def being(object):
pass
if __name__ == '__main__':
fake_decorator(Being)
fake_decorator(being)
運(yùn)行結(jié)果:
接收到的對象是:<class 'main.Being'>
接收到的對象是:<function being at 0x000001ED0B2876A8>
如果你看到了這個(gè)假裝是裝飾器的函數(shù)
感覺到了不削签舞,那就對了秕脓。但是TA不僅僅是假裝,TA真的是一款如假包換儒搭,低配版的裝飾器吠架,請看神奇的一刻:
def fake_decorator(obj):
print('接收到的對象是:{}'.format(obj))
return obj
@fake_decorator
class Being(object):
pass
@fake_decorator
def being():
pass
if __name__ == '__main__':
Being()
being()
運(yùn)行結(jié)果:
接收到的對象是:<class 'main.Being'>
接收到的對象是:<function being at 0x000001A8631376A8>
回想一下前面說的,裝飾器實(shí)質(zhì)就是一種函數(shù)(或類)的使用方式
搂鲫。當(dāng)被裝飾的函數(shù)對象(或類對象)被調(diào)用時(shí)傍药,該對象會被作為參數(shù)(param)傳入騎在ta身上的裝飾器
。
我們經(jīng)常可以看到的裝飾器多是基于函數(shù)創(chuàng)建的拐辽,長這樣:
# 這是基于函數(shù)的方式創(chuàng)建的無參數(shù)裝飾器
def func_based_decorator_without_params(original_func):
print('"_wrapper"瀏覽開始...')
@wraps(original_func)
def _wrapped_func(*_args, **_kwargs):
"""
裝飾作業(yè)層褪秀,為原始的function添加功能。
# functools.wraps裝飾器的作用是保留傳入的original_function的屬性薛训,
# 即經(jīng)過裝飾的函數(shù)屬性不會改變變?yōu)開wrapper返回的_wrapped_func媒吗。
"""
print('開始裝飾...')
print('傳入的函數(shù)為:{}'.format(original_func))
print('Function has been wrapped...')
method = original_func(*_args, **_kwargs)
print('What?趕緊干活乙埃!')
return method
print('"_wrapper"瀏覽結(jié)尾...')
return _wrapped_func
if __name__ == '__main__':
@func_based_decorator_without_params
def func_based_decorator_without_params(string):
print('不干活兒...')
func_based_decorator_without_params(string='hello_world')
運(yùn)行結(jié)果:
"_wrapper"瀏覽結(jié)尾...
開始裝飾...
傳入的函數(shù)為:<function func_based_decorator_without_params at 0x000001DA58962C80>
Function has been wrapped...
不干活兒...
What闸英?趕緊干活!
本文的主旨之一是介紹裝飾器是什么及用來做什么介袜,相信大家都已經(jīng)看出來甫何,這就是一個(gè)把原始對象傳入后對其各種騷操作,添加功能或者改變其功能遇伞,然后再傳出的工具辙喂,是的!
用類的方式是否可以寫出相同的功能呢鸠珠?必須可以巍耗。但是該壓軸內(nèi)容將會放在稍后介紹,我們緊接著討論另一種情況渐排,就是該裝飾器還需要傳參炬太。
# 比如有木有同學(xué)想起Flask中注冊路由的:
@xxx.route(method=['GET', 'POST'])
請看低配版?zhèn)鲄⒀b飾器的使用:
# 用于收集使用過decorator_with_params裝飾器的函數(shù)對象
collected_functions = []
# 這是基于函數(shù)的方式創(chuàng)建的傳參裝飾器
def func_based_decorator_with_params(*args, **kwargs):
"""
最外層,用于接收裝飾器的參數(shù)
"""
print('"_decorator"瀏覽開始...')
print('傳入的args有:{}'.format(args))
print('傳入的kargs有:{}'.format(kwargs))
def _wrapper(original_func):
"""
中間層驯耻,接收傳入的原始函數(shù)亲族。
"""
print('"_wrapper"瀏覽開始...')
@wraps(original_func)
def _wrapped_func(*_args, **_kwargs):
"""
裝飾作業(yè)層,為原始的function添加功能可缚。
"""
print('開始裝飾...')
collected_functions.append(original_func)
print('傳入的函數(shù)為:{}, 已存入collected_functions'.format(original_func))
print('傳入的參數(shù)有:args={}, kwargs={}'.format(_args, _kwargs))
print('Function has been wrapped...')
method = original_func(*_args, **_kwargs)
print('有活兒干了...')
return method
print('"_wrapper"瀏覽結(jié)尾...')
return _wrapped_func
print('"_decorator"瀏覽結(jié)尾...')
return _wrapper
if __name__ == '__main__':
@func_based_decorator_with_params('hello', serial_num='001')
def func_based_decorator_with_params_1(string):
print('不干活兒...')
@func_based_decorator_with_params('hello', serial_num='002')
def func_based_decorator_with_params_2(string):
print('不干活兒...')
func_based_decorator_with_params_1(string='hello_world')
func_based_decorator_with_params_2(string='hello_world')
print('collected_functions:{}'.format(collected_functions))
運(yùn)行結(jié)果:
"_decorator"瀏覽開始...
傳入的args有:('hello',)
傳入的kargs有:{'serial_num': '001'}
"_decorator"瀏覽結(jié)尾...
"_wrapper"瀏覽開始...
"_wrapper"瀏覽結(jié)尾...
"_decorator"瀏覽開始...
傳入的args有:('hello',)
傳入的kargs有:{'serial_num': '002'}
"_decorator"瀏覽結(jié)尾...
"_wrapper"瀏覽開始...
"_wrapper"瀏覽結(jié)尾...
開始裝飾...
傳入的函數(shù)為:<function func_based_decorator_with_params_1 at 0x000001E80E9A76A8>, 已存入collected_functions
傳入的參數(shù)有:args=(), kwargs={'string': 'hello_world'}
Function has been wrapped...
不干活兒...
有活兒干了...
開始裝飾...
傳入的函數(shù)為:<function func_based_decorator_with_params_2 at 0x000001E80E9A77B8>, 已存入collected_functions
傳入的參數(shù)有:args=(), kwargs={'string': 'hello_world'}
Function has been wrapped...
不干活兒...
有活兒干了...
collected_functions:[<function func_based_decorator_with_params_1 at 0x000001E80E9A76A8>, <function func_based_decorator_with_params_2 at 0x000001E80E9A77B8>]
現(xiàn)在我們來聊聊一個(gè)大主旨:
用類的方式寫裝飾器
該部分請各位同學(xué)重點(diǎn)關(guān)注一下傳參裝飾器霎迫,因?yàn)楣P者認(rèn)為嚴(yán)格意義上用類的方式寫的傳參解釋器是閹割版的,嗯帘靡,就是不完整的意思知给。。测柠。 請?jiān)谶\(yùn)行結(jié)果中密切關(guān)注是否有傳入代碼倒數(shù)第二行class_based_decorator_with_params函數(shù)的參數(shù)string='能看到我嗎'
的出現(xiàn)炼鞠。。轰胁。
# 把Ta們放在一起是為了讓大家能更方便地看清無傳參裝飾和有傳參裝飾器的比對區(qū)別谒主。
collected_functions = [] # 用于收集使用過decorator_with_params裝飾器的函數(shù)
class ClsBasedDecoratorWithoutParams(object):
"""
基于類的無傳參裝飾器。
"""
def __init__(self, original_func):
print('在基于類的裝飾器中...')
print('傳入的函數(shù)為:{}'.format(original_func))
self._func = original_func
def __call__(self, *args, **kwargs):
print('開始裝飾...')
print('傳入的參數(shù):args={}, kwargs={}'.format(args, kwargs))
collected_functions.append(self._func)
self._func(*args, **kwargs)
print('有活兒干了...')
return self._func
class ClsBasedDecoratorWithParams(object):
"""
基于類的傳參裝飾器赃阀。
"""
def __init__(self, *_args, **_kwargs):
print('在基于類的裝飾器中...')
print('裝飾器接收到的參數(shù)是:args={}, kwargs={}'.format(_args, _kwargs))
def __call__(self, original_func):
print('傳入的函數(shù)為:{}'.format(original_func))
collected_functions.append(original_func)
original_func()
print('有活兒干了...')
return original_func
if __name__ == '__main__':
@ClsBasedDecoratorWithoutParams # 無傳參裝飾器
def class_based_decorator_without_params(string):
print('不干活兒...')
class_based_decorator_without_params(string='hello_world')
@ClsBasedDecoratorWithParams('hello') # 有傳參裝飾器
def class_based_decorator_with_params(*args,**kwargs):
print('不干活兒...')
class_based_decorator_with_params(string='能看到我嗎')
print('collected_functions:{}'.format(collected_functions))
運(yùn)行結(jié)果:
在基于類的裝飾器中...
傳入的函數(shù)為:<function class_based_decorator_without_params at 0x00000272DEAF2C80>
開始裝飾...
傳入的參數(shù):args=(), kwargs={'string': 'hello_world'}
不干活兒...
有活兒干了...
在基于類的裝飾器中...
裝飾器接收到的參數(shù)是:args=('hello',), kwargs={}
傳入的函數(shù)為:<function class_based_decorator_with_params at 0x00000272DEB476A8>
不干活兒...
有活兒干了...
不干活兒...
collected_functions:[<function class_based_decorator_without_params at 0x0000012FAD332C80>, <function class_based_decorator_with_params at 0x0000012FAD3876A8>]
'能看到我嗎'
霎肯?我反正是沒看到擎颖,CPython解釋器中是否有機(jī)制可以讓我獲取這個(gè)參數(shù)(param)呢?如果你知道怎么使Ta不被閹割观游,感謝留言搂捧。
最后一個(gè)主題就是:
寫使用于類的裝飾器
其實(shí)本小文的開頭,咱們已經(jīng)實(shí)現(xiàn)了這個(gè)功能懂缕,請看最開始的代碼示例允跑。簡單地說,只要能騎上哪個(gè)object搪柑,就能傳ta進(jìn)自己肚子里聋丝。。工碾。 由于類經(jīng)常使用Mixin抽象類的多繼承來實(shí)現(xiàn)功能擴(kuò)展弱睦,在實(shí)際開發(fā)中筆者個(gè)人并無用到這種方式來擴(kuò)展或者改變類中的方法。
接下來渊额,我就用類的方式來實(shí)現(xiàn)一個(gè)簡單的用于類的裝飾器吧况木。
class ClsUsingDecoratorWithoutParams(object):
"""
使用于類的無傳參裝飾器。
"""
def __init__(self, original_cls):
print('在適用于類的裝飾器中...')
self._cls = original_cls
print(self._cls)
def __call__(self, *args, **kwargs):
print('在適用于類的裝飾器的__call__中...')
print('接收到的參數(shù):args={}, kwargs={}'.format(args, kwargs))
return self._cls(*args, **kwargs)
class ClsUsingDecoratorWithParams(object):
"""
使用于類的傳參裝飾器旬迹。
"""
def __init__(self, *_args, **_kwargs):
print('在基于類的裝飾器中...')
print('裝飾器接收到的參數(shù)是:args={}, kwargs={}'.format(_args, _kwargs))
def __call__(self, original_cls):
print('傳入的類為:{}'.format(original_cls))
print('有活兒干了...')
return original_cls
if __name__ == '__main__':
@ClsUsingDecoratorWithoutParams
class ClsUsingTestingI(object):
def __init__(self, *args, **kwargs):
print('ClsUsingTestingI接收到的參數(shù):args={}, kwargs={}'.format(args, kwargs))
cls_using_obj_i = ClsUsingTestingI(string='HelloWorld')
print(cls_using_obj_i)
@ClsUsingDecoratorWithParams('Hi')
class ClsUsingTestingII(object):
def __init__(self, *args, **kwargs):
self.kwargs = kwargs
print('ClsUsingTestingII接收到的參數(shù):args={}, kwargs={}'.format(args, kwargs))
cls_using_obj_ii = ClsUsingTestingII(string='HelloWorld')
print('cls_using_obj_ii:{}'.format(cls_using_obj_ii))
print('cls_using_obj_ii.kwargs:{}'.format(cls_using_obj_ii.kwargs))
運(yùn)行結(jié)果:
在適用于類的裝飾器中...
<class 'main.ClsUsingTestingI'>
在適用于類的裝飾器的call中...
接收到的參數(shù):args=(), kwargs={'string': 'HelloWorld'}
ClsUsingTestingI接收到的參數(shù):args=(), kwargs={'string': 'HelloWorld'}
<main.ClsUsingTestingI object at 0x000002337298D748>
在基于類的裝飾器中...
裝飾器接收到的參數(shù)是:args=('Hi',), kwargs={}
傳入的類為:<class 'main.ClsUsingTestingII'>
有活兒干了...
ClsUsingTestingII接收到的參數(shù):args=(), kwargs={'string': 'HelloWorld'}
cls_using_obj_ii:<main.ClsUsingTestingII object at 0x000002337298D780>
cls_using_obj_ii.kwargs:{'string': 'HelloWorld'}
感謝您的支持和打賞 (= ̄ω ̄=) -->
BuyMeACoffee(Paypal)
如果喜歡這篇文章火惊,記得收藏和點(diǎn) ? 噢 ~ ~