Python中的裝飾器垒在、類裝飾器(用類的方式寫)及用于類的裝飾器(裝飾在類上)

裝飾器是非常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) ? 噢 ~ ~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市舱权,隨后出現(xiàn)的幾起案子矗晃,更是在濱河造成了極大的恐慌仑嗅,老刑警劉巖宴倍,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仓技,居然都是意外死亡鸵贬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門脖捻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阔逼,“玉大人,你說我怎么就攤上這事地沮∈雀。” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵摩疑,是天一觀的道長危融。 經(jīng)常有香客問我,道長雷袋,這世上最難降的妖魔是什么吉殃? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上蛋勺,老公的妹妹穿的比我還像新娘瓦灶。我一直安慰自己,他們只是感情好抱完,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布贼陶。 她就那樣靜靜地躺著,像睡著了一般巧娱。 火紅的嫁衣襯著肌膚如雪每界。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天家卖,我揣著相機(jī)與錄音眨层,去河邊找鬼。 笑死上荡,一個(gè)胖子當(dāng)著我的面吹牛趴樱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酪捡,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼叁征,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逛薇?” 一聲冷哼從身側(cè)響起捺疼,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎永罚,沒想到半個(gè)月后啤呼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呢袱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年官扣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羞福。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惕蹄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出治专,到底是詐尸還是另有隱情卖陵,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布张峰,位于F島的核電站泪蔫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挟炬。R本人自食惡果不足惜鸥滨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一嗦哆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧婿滓,春花似錦老速、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卿吐,卻和暖如春旁舰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗡官。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工箭窜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衍腥。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓磺樱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親婆咸。 傳聞我的和親對象是個(gè)殘疾皇子竹捉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容