這篇文章主要為大家展示了“Python中裝飾器的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Python中裝飾器的示例分析”這篇文章吧。
網(wǎng)站設(shè)計(jì)制作過程拒絕使用模板建站;使用PHP+MYSQL原生開發(fā)可交付網(wǎng)站源代碼;符合網(wǎng)站優(yōu)化排名的后臺管理系統(tǒng);網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)收費(fèi)合理;免費(fèi)進(jìn)行網(wǎng)站備案等企業(yè)網(wǎng)站建設(shè)一條龍服務(wù).我們是一家持續(xù)穩(wěn)定運(yùn)營了10年的創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司。需要理解的一些概念
要理解Python中的裝飾器,我覺得還是應(yīng)該從最基本的概念開始:
裝飾器模式:所謂的裝飾器模式,可以簡單地理解為“在不改變原有內(nèi)部實(shí)現(xiàn)的情況下,為函數(shù)或者類添加某種特性”。這樣我們就可以將一些與業(yè)務(wù)無關(guān)、具有通用性的代碼抽象出來,作為裝飾器附加到需要這些代碼的函數(shù)或者類之上。用面向切面編程的思想解釋就是“裝飾器應(yīng)該是一個切面”。
函數(shù)是一等公民:意思就是函數(shù)可以被當(dāng)成普通變量一樣使用。在Python中,可以把函數(shù)賦值給變量,可以將函數(shù)作為其它函數(shù)的參數(shù),也可以將函數(shù)作為其它函數(shù)的返回值。
閉包:我們都知道局部作用域可以引用全局作用域中的變量,相似的,當(dāng)一個函數(shù)內(nèi)部又定義了其它函數(shù)的時候,內(nèi)部函數(shù)可以使用外部函數(shù)所在作用域的變量,這就是閉包。
從最簡單的裝飾器做起
理解完以上的概念之后,我們嘗試一下利用這些特性實(shí)現(xiàn)一個簡單的裝飾器。
首先明確一下需求,我們有時候會需要在函數(shù)調(diào)用時打印一個相應(yīng)的日志,雖然可以通過在所有需要打印日志的函數(shù)代碼中嵌入打印日志的代碼來實(shí)現(xiàn),但這種方法不僅增加了許多重復(fù)代碼,而且在業(yè)務(wù)代碼中嵌入與業(yè)務(wù)無關(guān)的代碼增加了整體的耦合度。因此,我們需要實(shí)現(xiàn)一個裝飾器,這個裝飾器在函數(shù)調(diào)用時可以打印一個日志記錄函數(shù)調(diào)用行為。
如果我們有以下函數(shù)foo,代表具體的業(yè)務(wù)函數(shù):
def foo(): print('in function foo')
我們設(shè)想通過調(diào)用foo = deco(foo)實(shí)現(xiàn)給函數(shù)foo增加打印日志的功能,并且不影響它原有的業(yè)務(wù)。那么在這種設(shè)想下,裝飾器deco應(yīng)該也是一個函數(shù),它接收另一個函數(shù)作為輸入,并返回一個新的、經(jīng)過裝飾的函數(shù)。在Python中,我們可以這么寫:
def deco(func): # 接收一個函數(shù)作為參數(shù) def new_func(): print(f'[log] run function {func.__name__}') # 此處使用了Python3.6的格式化字符串 func() # 閉包,在內(nèi)部函數(shù)中使用了外部函數(shù)的變量 return new_func # 將新函數(shù)作為返回值返回
執(zhí)行一下試試效果:
>>> foo = deco(foo) >>> foo() [log] run function foo in function foo
不錯,至此我們已經(jīng)實(shí)現(xiàn)了一個最簡單的裝飾器!在上面的代碼中,裝飾器deco接收任意的函數(shù)作為參數(shù),再在內(nèi)部構(gòu)造另一個函數(shù),利用閉包的特性,可以在新函數(shù)里調(diào)用存在于裝飾器deco局部作用域中的函數(shù)func。
神奇的@
按照上面那么寫,每次我們都得為需要裝飾的函數(shù)賦一個新值,萬一函數(shù)或者裝飾器的數(shù)量增加了,手動寫賦值和函數(shù)調(diào)用就會變得非常麻煩。那么在Python中,有沒有更優(yōu)雅的寫法呢?答案是有的,你只需要一個@符號。
在Python中,當(dāng)我們需要一個裝飾器時:
def deco(func): def new_func(): print(f'[log] run function {func.__name__}') func() return new_func @deco def foo(): print('in function foo')
這個地方我們省略了函數(shù)的賦值,直接在函數(shù)foo定義的上一行加上@deco進(jìn)行裝飾。運(yùn)行一下試試看:
>>> foo() [log] run function foo in function foo
是不是感覺很神奇?其實(shí)這里面沒什么魔法,只不過是Python在處理函數(shù)定義的代碼時,幫你在其中把foo=deco(foo)的邏輯加上了而已。
裝飾器也想要參數(shù)
上面的代碼實(shí)現(xiàn)了在業(yè)務(wù)函數(shù)調(diào)用之前打印日志的功能,那如果我們需要在業(yè)務(wù)代碼執(zhí)行完之后打印一條自定義的消息,怎么辦呢?我們必須要讓我們的裝飾器可以接收自定義參數(shù)。
上面提到過,Python做的只是當(dāng)寫了@deco時,把調(diào)用deco(func)的結(jié)果賦值給它裝飾的函數(shù)而已。順著這個邏輯,當(dāng)我們需要一個帶參數(shù)的裝飾器時,代碼上應(yīng)該是寫為@deco('some message'),這時Python將調(diào)用deco(msg)(func)的結(jié)果賦值給foo。那么事情就變得簡單了,我們只需要在上面代碼的基礎(chǔ)上嵌套一層函數(shù):
def deco(msg): def inner_deco(func): def new_func(): print(f'[log] run function {func.__name__}') func() print(f'[log] {msg}') return new_func return inner_deco @deco('some message') def foo(): print('in function foo')
執(zhí)行一下試試:
>>> foo() [log] run function foo in function foo [log] some message
不支持帶參數(shù)的被裝飾函數(shù)的裝飾器不是好裝飾器
上面的代碼還是有問題,因?yàn)槲覀冎豢紤]了函數(shù)foo沒有參數(shù)時的情況,萬一函數(shù)foo帶了參數(shù),這個裝飾器就會丟失參數(shù)信息,這不是一個合格的裝飾器應(yīng)該出現(xiàn)的情況。所以,我們借助Python中的*args和**kwargs使被裝飾的函數(shù)可以支持傳入任意參數(shù):
def deco(msg): def inner_deco(func): def new_func(*args, **kwargs): print(f'[log] run function {func.__name__}') func(*args, **kwargs) print(f'[log] {msg}') return new_func return inner_deco @deco('some message') def foo(a, b=None): print('in function foo') print(f'a is {a} & b is ')
這樣一來,無論函數(shù)foo的參數(shù)列表是怎么樣的都不會有問題了:
>>> foo('hello') [log] run function foo in function foo a is hello & b is None [log] some message
不支持有返回值的被裝飾函數(shù)的裝飾器不是好裝飾器
別忘了,到目前為止,我們寫的函數(shù)foo都沒有返回值,如果函數(shù)foo有返回值怎么辦呢?我想你心里應(yīng)該有答案了:
def deco(msg): def inner_deco(func): def new_func(*args, **kwargs): print(f'[log] run function {func.__name__}') rlt = func(*args, **kwargs) print(f'[log] {msg}') return rlt return new_func return inner_deco @deco('some message') def foo(a, b=None): print('in function foo') print(f'a is {a} & b is ') return 'ok'
由于裝飾器在原函數(shù)執(zhí)行完之后還有別的操作,所以應(yīng)該把返回值暫存起來,等到裝飾器的邏輯執(zhí)行完畢,才返回最終結(jié)果。這就是我們的最終版裝飾器了!
>>> rlt = foo('a') [log] run function foo in function foo a is a & b is None [log] some message >>> print(rlt) ok
有沒有更騷的操作?
當(dāng)然有啊!我標(biāo)題都這么寫了難不成會沒有?
在Python中,你可以使用類來作為裝飾器:
class Deco(object): def __call__(self, func): def new_func(): print(f'[log] run function {func.__name__}') func() return new_func @Deco() def foo(): print('in function foo')
>>> foo() [log] run function foo in function foo
這么做的好處就是可以利用類更好地管理參數(shù)和調(diào)用邏輯,比起之前三層函數(shù)嵌套的形式是不是清晰多了?
在Python中,你還可以使用裝飾器來裝飾一個類,比如這樣:
def add_doc(doc): def deco(cls): cls.__doc__ = doc return cls return deco @add_doc('this is the doc of Cls') class Cls(object): pass
來看看效果:
>>> help(Cls) Help on class Cls in module __main__: class Cls(builtins.object) | this is the doc of Cls
上面的代碼只是一個示例,展示裝飾器怎么裝飾一個類而已,不是說在實(shí)際情況下應(yīng)該這么用。大部分的情況下,我們對于類的拓展應(yīng)該是通過繼承而不是裝飾。
具體怎么巧妙地利用裝飾器就要靠大家發(fā)揮自己的想象力了。
以上是“Python中裝飾器的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計(jì)公司行業(yè)資訊頻道!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
名稱欄目:Python中裝飾器的示例分析-創(chuàng)新互聯(lián)
當(dāng)前URL:http://jinyejixie.com/article10/djsggo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊、全網(wǎng)營銷推廣、品牌網(wǎng)站設(shè)計(jì)、關(guān)鍵詞優(yōu)化、網(wǎng)站營銷、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容