Python語法詳解之decorator裝飾器
python 是一門優(yōu)雅的語言,有些使用方法就像魔法一樣。裝飾器(decorator)就是一種化腐朽性為神奇的技巧。最近一直都在使用 Tornado 框架,一直還是念念不忘 Flask 。Flask 是我最喜歡的 Python 框架,最早被它吸引也是源自它使用裝飾器這個語法糖(Syntactic sugar)來做 Router,讓代碼看上去就感覺甜甜的。
Tornado 中的 Router 略顯平淡,懷念 Flask 的味道,于是很好奇的想知道 Flask 是如何使用這個魔法。通過閱讀 Flask 的源碼,我們也可以為 Tornado 實現(xiàn)了一個裝飾器 Router。
當(dāng)然對于剛接觸 Python 的人,也許很容易理解裝飾器本質(zhì)是設(shè)計模式中的裝飾器模式??墒?Python 通過@一個實現(xiàn)裝飾器的語法糖。下面看下Python語法詳解之decorator裝飾器。
一、定義
裝飾器 decorator 或者稱為包裝器,是對函數(shù)的一種包裝。
二、作用
它能使函數(shù)的功能得到擴(kuò)充,而同時不用修改函數(shù)本身的代碼。它能夠增加函數(shù)執(zhí)行前、執(zhí)行后的行為,而不需對調(diào)用函數(shù)的代碼做任何改變。
三、舉例
初始化函數(shù)
# 函數(shù)hello,輸出 hello + name 的字符串 def hello(name): return 'hello ' + name
希望實現(xiàn)功能:在每一個調(diào)用 hello 函數(shù)的時候,將輸出的字符串用 <tag>包住
例如:hello john 變成 <tag>hello john<tag>
方法一:自定義wrapper函數(shù)
這種方法成功修改了函數(shù) hello 的行為,不過需要修改對 hello的調(diào)用。
每一個調(diào)用hello 的地方,都要給成調(diào)用wrapper,并修改參數(shù)列表
def hello(name): return 'hello ' + name def wrapper(tag, func, *arg, **kvargs): tag = "<" + tag + ">" return tag + func(*arg, **kvargs) + tag if __name__ == "__main__": print(wrapper('p', hello, 'john'))
輸出
方法二:自定義decorator函數(shù)
為了不改變對 hello的調(diào)用。我們需要得到一個新的函數(shù)對象,它修改 hello的行為,并用這個對象對 hello賦值。
從而調(diào)用 hello的時候,調(diào)用的是擴(kuò)充行為后的 hello
def hello(name): return 'hello ' + name def myDecorator(func, tag): def myWrapper(*arg, **kvargs): # 重新包裝func,其參數(shù)列表與func一致 sign = "<" + tag + ">" return sign + func(*arg, **kvargs) + sign return myWrapper hello = myDecorator(hello, "div") # 用新的函數(shù)對象修改hello if __name__ == "__main__": print(hello("john"))
這樣,只要hello被myDecorator 賦值一次,以后再調(diào)用hello 時,就調(diào)用的是包裝后的函數(shù)
輸出
方法三:python的decorator
python 的裝飾器所做的事與方式2類似
它通過語法糖使裝飾器看起來更清晰、簡介,而不用每次都書寫方式2中第7行代碼 hello = myDecorator(hello, "div")
def setTag(tag): # 由于此裝飾器需要參數(shù),所以要再套一層 def myDecorator(func): # 裝飾器的核心,接受函數(shù)對象做參數(shù),返回包裝后的函數(shù)對象 def myWrapper(*arg, **kvargs): # 包裝的具體過程 sign = "<" + tag + ">" return sign + func(*arg, **kvargs) + sign return myWrapper return myDecorator @setTag("div") # 用@標(biāo)簽在定義函數(shù)時套上裝飾器 def hello(name): return 'hello' + name if __name__ == '__main__': print(hello('john'))
本質(zhì)上,方式2 與 方式3 完成的是同一件事,只不過方式3 比方式2 代碼更簡潔,方便。
比如,現(xiàn)在要給 hello 函數(shù)套上三個標(biāo)簽<body><div><p>
如果用方式2
hello = myDecorator(myDecorator(myDecorator(hello, "body"),"div"),"p")
如果用方式3
@myDecorator("body") @myDecorator("div") @myDecorator("p") def hello(name) return 'hello' + name
在多個裝飾器嵌套的情況下,python內(nèi)置的decorator 結(jié)構(gòu)更清晰。
偽代碼:
def myDecorator(...): #定義裝飾器,可能帶參數(shù) def decorator(func): #裝飾器核心,以被裝飾的函數(shù)對象為參數(shù),返回裝飾后的函數(shù)對象 def wrapper(*args, **kvargs): #裝飾的過程,參數(shù)列表適應(yīng)不同參數(shù)的函數(shù) ... #修改函數(shù)調(diào)用前的行為 func(*args, **kvargs) #調(diào)用函數(shù) ... #修改函數(shù)調(diào)用后的行為 return wrapper return decorator @myDecorator(...): #給函數(shù)加上裝飾器 def myFunc(...): #自己定義的功能函數(shù) ...
知識點:
- 在python中,當(dāng)*和**符號出現(xiàn)在函數(shù)定義的參數(shù)中時,表示任意數(shù)目參數(shù)收集。*arg表示任意多個無名參數(shù),類型為tuple;**kwargs表示關(guān)鍵字參數(shù),為dict,使用時需將*arg放在**kwargs之前,否則會有“SyntaxError: non-keyword arg after keyword arg”的語法錯誤
- 在函數(shù)調(diào)用時,*會以單個元素的形式解包一個元祖,使其成為獨立的參數(shù)。
- 在函數(shù)調(diào)用時,**會以鍵/值對的形式解包一個字典,使其成為獨立的關(guān)鍵字參數(shù)。
到此這篇關(guān)于Python語法:decorator裝飾器的文章就介紹到這了,更多相關(guān)Python decorator裝飾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 基于DDT實現(xiàn)數(shù)據(jù)驅(qū)動測試
這篇文章主要介紹了python 基于DDT實現(xiàn)數(shù)據(jù)驅(qū)動測試的方法,幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下2021-02-02使用Python對EXCEL數(shù)據(jù)的預(yù)處理
這篇文章主要介紹了使用Python處理EXCEL基礎(chǔ)操作篇2,如何使用Python對EXCEL數(shù)據(jù)的預(yù)處理,文中提供了解決思路和部分實現(xiàn)代碼,一起來看看吧2023-03-03Python使用指定字符長度切分?jǐn)?shù)據(jù)示例
今天小編就為大家分享一篇Python使用指定字符長度切分?jǐn)?shù)據(jù)示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12tensorflow學(xué)習(xí)筆記之簡單的神經(jīng)網(wǎng)絡(luò)訓(xùn)練和測試
這篇文章主要為大家詳細(xì)介紹了tensorflow學(xué)習(xí)筆記,用簡單的神經(jīng)網(wǎng)絡(luò)來訓(xùn)練和測試,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04python 創(chuàng)建彈出式菜單的實現(xiàn)代碼
這篇文章主要介紹了python 創(chuàng)建彈出式菜單的實現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-07-07