Python裝飾器(4)帶參數(shù)的裝飾器

內(nèi)容純屬個人理解,不對之處,歡迎指正。

之前說過,裝飾器其實就是函數(shù),既然是函數(shù),那就可以有參數(shù),裝飾器也不例外,接下來我們來分析帶參數(shù)的裝飾器。

如何構(gòu)造帶參數(shù)

帶參數(shù)倒是很簡單,在裝飾的時候給裝飾函數(shù)寫上參數(shù)就行,但是具體的裝飾器函數(shù)該怎么寫,我們需要思考一下。
我們想讓它帶參數(shù),無非就是對被裝飾函數(shù)的執(zhí)行進(jìn)行一定條件的限定設(shè)置,也就是說在函數(shù)執(zhí)行之前,函數(shù)就必須具備這個條件,那么這樣的話,就有兩種選擇,要么在傳遞func以后傳遞參數(shù);要么在傳遞func之前傳遞參數(shù)。
在想法上,兩種其實是都可以的,但是,我們傳遞參數(shù)的時候是給裝飾器傳遞,也就是說,裝飾器函數(shù)首先接收的是裝飾器函數(shù)的參數(shù),然后帶著這個參數(shù)再去裝飾函數(shù)。因此,我們就必須先處理裝飾器函數(shù)的參數(shù),然后再去處理被裝飾的函數(shù)。
所以在實現(xiàn)上,我們應(yīng)該這樣去構(gòu)造帶參裝飾器函數(shù):

def deco_para(parameter):
    def deco_func(func):
        def wrapper(*args, **kwargs):
            print(parameter)
            func(*args, **kwargs)
        return wrapper
    return deco_func

帶參裝飾器示例

def deco_para(parameter):
    print('enter deco_para')

    def deco_func(func):
        print('enter deco_func')

        def wrapper(*args, **kwargs):
            print('enter wrapper')
            print(parameter)
            print('---wrapper: before func---')
            func(*args, **kwargs)
            print('---wrapper: after func---')

        return wrapper

    return deco_func


@deco_para(123)
def foo():
    print('---foo---')


if __name__ == '__main__':
    print('--start--')
    foo()

運行結(jié)果:

enter deco_para
enter deco_func
--start--
enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---

結(jié)果應(yīng)該不用多說,先接收參數(shù)123,然后接收函數(shù)foo,最后執(zhí)行wrapper。

裝飾過程解析--多次輸出問題

我們解析下過程

deco_func = deco_para(123)  # 接收參數(shù)123
wrapper = deco_func(foo)  # 接收函數(shù)foo
foo = wrapper  # 重命名
foo()  # 執(zhí)行foo

如果你試著按這個過程去執(zhí)行代碼,會發(fā)現(xiàn)一個問題,wrapper函數(shù)里面的代碼執(zhí)行了2次。

enter deco_para
enter deco_func
enter deco_para
enter deco_func
enter wrapper
123
---wrapper: before func---
enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---
---wrapper: after func---

我們看下究竟是怎么回事。

# 程序執(zhí)行,掃面到裝飾器,執(zhí)行裝飾器函數(shù)內(nèi)部代碼
1.enter deco_para
2.enter deco_func

# deco_para(123)接收參數(shù)123時執(zhí)行3
3.enter deco_para

# deco_func(foo)接收函數(shù)foo時執(zhí)行4
4.enter deco_func

# foo()執(zhí)行foo()
5.enter wrapper
6.123
7.---wrapper: before func---

8.enter wrapper
9.123
10.---wrapper: before func---
11.---foo---
12.---wrapper: after func---

13.---wrapper: after func---

在之前解析裝飾器的時候就提到過,函數(shù)被裝飾以后,就不是原來的函數(shù)了,也就是說上面所執(zhí)行的foo,其實是wrapper。
那么wrapper里面有什么東西呢?

print('enter wrapper')
print(parameter)
print('---wrapper: before func---')
func(*args, **kwargs)
print('---wrapper: after func---')

似乎執(zhí)行結(jié)果應(yīng)該是

enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---

但是事實上,此時的wrapper里面的func已經(jīng)不是原來的func。
回顧閉包:引用了自由變量的函數(shù)即是一個閉包,這個被引用的自由變量和這個函數(shù)一同存在, 即使已經(jīng)離開了了創(chuàng)造它的環(huán)境也不例外
那么可以推測,此時的被裝飾函數(shù)應(yīng)該具有了額外的東西,這些東西就是

print('enter wrapper')
print(parameter)
print('---wrapper: before func---')
print('---wrapper: after func---')

由此也就可以知道為什么wrapper里面的代碼執(zhí)行了2次:就是在執(zhí)行到func(*args, **kwargs)的時候,執(zhí)行了功能豐富以后的func。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 包(lib)、模塊(module) 在Python中,存在包和模塊兩個常見概念。 模塊:編寫Python代碼的py...
    清清子衿木子水心閱讀 3,912評論 0 27
  • 每個人都有的內(nèi)褲主要功能是用來遮羞,但是到了冬天它沒法為我們防風(fēng)御寒,咋辦?我們想到的一個辦法就是把內(nèi)褲改造一下,...
    chen_000閱讀 1,403評論 0 3
  • 陸焉識與馮婉喻的故事看完了。我的心有著微微刺痛感。 一份愛情,等到滿頭白發(fā),等到痛徹心扉,才發(fā)現(xiàn),這,原來就是愛。...
    yayajy閱讀 300評論 0 0
  • 今天是個好日子
    薇薇攝影閱讀 222評論 0 0
  • 處理結(jié)果 mysql_fetch_assoc( ) 返回關(guān)聯(lián)數(shù)組 mysql_fetch_row( ) 返回索...
    FKTX閱讀 707評論 0 0

友情鏈接更多精彩內(nèi)容