Python基礎(chǔ)手冊25——裝飾器

一晃择、裝飾器

裝飾器背后的主要動機(jī)源自 python 面向?qū)ο缶幊獭Qb飾器是在函數(shù)調(diào)用之上的修飾罪佳。這些修飾僅是當(dāng)聲明一個(gè)函數(shù)或者方法的時(shí)候却舀,才會應(yīng)用的額外調(diào)用虫几。

裝飾器的語法以 @ 開頭,接著是裝飾器函數(shù)的名字和可選的參數(shù)挽拔。緊跟著裝飾器聲明的是被修飾的函數(shù)辆脸,和裝飾函數(shù)的可選參數(shù)。

裝飾器看起來會是這樣:

此外螃诅,裝飾器可以如函數(shù)調(diào)用一樣“堆疊“起來啡氢,這里有一個(gè)更加普遍的例子,使用了多個(gè)裝飾器:


有參數(shù)和無參數(shù)的裝飾器

沒有參數(shù)的裝飾器:

帶參數(shù)的裝飾器 :

現(xiàn)在我們知道裝飾器實(shí)際就是函數(shù)术裸。我們也知道他們接受函數(shù)對象倘是。一般說來,當(dāng)你包裝一個(gè)函數(shù)的時(shí)候袭艺,你可以在包裝的環(huán)境下在合適的時(shí)機(jī)調(diào)用這個(gè)函數(shù)辨绊。我們在執(zhí)行函數(shù)之前,可以運(yùn)行些預(yù)備代碼匹表,如 post-morrem 分析,也可以在執(zhí)行代碼之后做些清理工作宣鄙。

你可以考慮在裝飾器中置入通用功能的代碼來降低程序復(fù)雜度袍镀。例如,可以用裝飾器來:

  • 引入日志
  • 增加計(jì)時(shí)邏輯來檢測性能
  • 給函數(shù)加入事務(wù)的能力

對于用 python 創(chuàng)建企業(yè)級應(yīng)用冻晤,支持裝飾器的特性是非常重要的苇羡。


二、裝飾器的原理探析

我們可以跟著下面的例子更深層次的理解裝飾器的原理鼻弧。

當(dāng)你要在一個(gè)函數(shù)A之前或者之后增加一些操作的話设江,我們可以定義一個(gè)新的函數(shù)B,在函數(shù)B中定義這些操作攘轩,并在指定的位置調(diào)用函數(shù)A叉存。這樣我們就等到了一個(gè)新的函數(shù)對象,他封裝的對函數(shù)A執(zhí)行的額外的操作度帮。并且可以把B函數(shù)名賦值給其他變量歼捏。

但是現(xiàn)在我們有一類函數(shù)稿存,想要跟函數(shù)A一樣增加同樣的操作,這是我們就可以把這些函數(shù)作為參數(shù)傳入函數(shù)B中瞳秽,然后在指定的位置通過小括號來調(diào)用傳入的函數(shù)對象瓣履。這樣我們的函數(shù)B就有了參數(shù)。這樣我們就無法把帶參數(shù)的函數(shù)B賦值給新的變量名练俐,因?yàn)榘岩粋€(gè)參數(shù)傳給函數(shù)袖迎,就會調(diào)用這個(gè)函數(shù)。

當(dāng)然我們可以在全局作用域中定義func變量腺晾。

這時(shí)如果我們想用一個(gè)變量來保存調(diào)用不同函數(shù)的B函數(shù)燕锥,就會出現(xiàn)所有的變量都只會調(diào)用同一個(gè)函數(shù),也就是func最后賦值的函數(shù)丘喻。

那怎樣將func和B綁定到一個(gè)作用域呢脯宿?讓他們綁定到一起成為一個(gè)新的函數(shù)對象返回。要知道上面的例子中b和b2都是指向了統(tǒng)一個(gè)對象B泉粉,所以他們的調(diào)用結(jié)果才都是一樣的连霉。

這時(shí)我們可以利用嵌套函數(shù)的一個(gè)特點(diǎn),就是 在嵌套函數(shù)中嗡靡,將內(nèi)層函數(shù)對象作為返回值返回的話跺撼,內(nèi)層函數(shù)對象可以保留其所在的作用域(外層函數(shù)的作用域),也就是外層函數(shù)中定義的一切變量都可以跟隨內(nèi)層函數(shù)得到保留讨彼。 利用這個(gè)特性歉井,因?yàn)樾螀⒁蔡幱诤瘮?shù)作用域中,所以我們讓外層函數(shù)來接受參數(shù)哈误,當(dāng)外層函數(shù)調(diào)用結(jié)束后哩至,返回的內(nèi)層函數(shù)還處于一個(gè)封閉的作用域中,并可以使用外層函數(shù)的參數(shù)蜜自。

按照上面的思路菩貌,我們可以在函數(shù)B的外層再封裝一層函數(shù)C,讓外層的函數(shù)C來接受參數(shù)重荠,而函數(shù)B不用自己傳入?yún)?shù)箭阶,直接使用外層的函數(shù)C就可以了。這樣我們可以在外層的函數(shù)C中直接將使用了外層函數(shù)C參數(shù)的函數(shù)B對象返回戈鲁。這樣我們把A對象作為參數(shù)傳遞給外層函數(shù)C仇参,將其調(diào)用,但可以接受到一個(gè)綁定了形參func(這里就是A)的新函數(shù)B對象婆殿。

注意這里的函數(shù)對象B和單純定義的函數(shù)B是不一樣的诈乒,因?yàn)樗蛥?shù)func綁定在了一起,是一個(gè)新的函數(shù)對象鸣皂。所以上面b和b2是兩個(gè)各自獨(dú)立的函數(shù)對象抓谴。

裝飾器就是為了解決上面的問題而存在的暮蹂,我們使用@符號為函數(shù)加上裝飾器。

當(dāng)然我們函數(shù)A本身也是可以有參數(shù)的癌压,那么在函數(shù)C中我們最后返回的函數(shù)B對象也應(yīng)該定義對應(yīng)的參數(shù)才行仰泻,那么為了為了通用性,我們可以使用可擴(kuò)展參數(shù)滩届,也就是在B函數(shù)中只定義 *args 和 kwargs集侯, args用來接收所有的位置參數(shù),kwargs用來節(jié)后所有的關(guān)鍵字參數(shù)帜消。在B的代碼中棠枉,我們再將其解包以args 和 **kwargs 的形式傳給函數(shù)func 的調(diào)用。

當(dāng)然我們的函數(shù)B本身也有可能需要一些參數(shù)來實(shí)現(xiàn)某些功能泡挺。我們可以通過直接在函數(shù)B中直接定義參數(shù)來實(shí)現(xiàn)辈讶。但是裝飾器的語法并不支持這樣操作,他不能自動把裝飾器接受到的參數(shù)直接傳給B中的參數(shù)娄猫,而且還不影響 *args 和 **kwargs 正常傳值贱除。所以Python為了將B函數(shù)接受使用的參數(shù)和內(nèi)層調(diào)用使用的函數(shù)的參數(shù)區(qū)分開來,又再次使用利用了嵌套函數(shù)的特點(diǎn)媳溺。我們可以B接受使用的參數(shù)放在外層函數(shù)的作用域中月幌,但是外層函數(shù)接受了 func 作為參數(shù),為了避免 func 參數(shù)和這些參數(shù)相互影響悬蔽,所以可以把這些參數(shù)又放在了外外層的函數(shù)的作用域中扯躺,所以我們需要在外層函數(shù)外在嵌套一層函數(shù)來接受函數(shù)B需要的參數(shù)。

在函數(shù)C外面再嵌套一層函數(shù)的方法蝎困, 不僅是因?yàn)槲覀兩厦嬲f的將B需要的參數(shù)和func 變量的作用域隔離開來录语。這里,我們再分析一下裝飾器的語法禾乘,裝飾器是在需要改造的函數(shù)的上面加一個(gè)@符號后面跟一個(gè)我們定義的改造函數(shù)名钦无。這個(gè)@加函數(shù)名的語法其實(shí)跟小括號一樣,會直接調(diào)用這個(gè)函數(shù)盖袭,并把下面裝飾的函數(shù)作為參數(shù)傳入。那么我們要給裝飾器傳入?yún)?shù)時(shí)彼宠,需要使用小括號鳄虱,那么小括號也是執(zhí)行函數(shù)的表達(dá)式。所以這個(gè)裝飾器函數(shù)在匹配上小括號和@符號時(shí)要被執(zhí)行兩次凭峡。而且是小括號先執(zhí)行拙已。所以我們必須在裝飾器函數(shù)中進(jìn)行兩次嵌套。這樣小括號表達(dá)式執(zhí)行了函數(shù)后將要返回一個(gè)函數(shù)對象C摧冀,C函數(shù)對象和@符號匹配將下面裝飾的函數(shù)A作為參數(shù)傳入倍踪,再次執(zhí)行并返回一個(gè)新的函數(shù)對象B系宫,并賦值給下面函數(shù)同名的變量名A。

注意@符號和() 都是函數(shù)調(diào)用語法建车。給一個(gè)函數(shù)加上裝飾器扩借,Python解釋器會直接執(zhí)行裝飾器函數(shù)。最后生成一個(gè)新的函數(shù)對象缤至,也就是上面例子中的A潮罪。


《Python基礎(chǔ)手冊》系列:

Python基礎(chǔ)手冊 1 —— Python語言介紹
Python基礎(chǔ)手冊 2 —— Python 環(huán)境搭建(Linux)
Python基礎(chǔ)手冊 3 —— Python解釋器
Python基礎(chǔ)手冊 4 —— 文本結(jié)構(gòu)
Python基礎(chǔ)手冊 5 —— 標(biāo)識符和關(guān)鍵字
Python基礎(chǔ)手冊 6 —— 操作符
Python基礎(chǔ)手冊 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊 8 —— Python對象
Python基礎(chǔ)手冊 9 —— 數(shù)字類型
Python基礎(chǔ)手冊10 —— 序列(字符串)
Python基礎(chǔ)手冊11 —— 序列(元組&列表)
Python基礎(chǔ)手冊12 —— 序列(類型操作)
Python基礎(chǔ)手冊13 —— 映射(字典)
Python基礎(chǔ)手冊14 —— 集合
Python基礎(chǔ)手冊15 —— 解析
Python基礎(chǔ)手冊16 —— 文件
Python基礎(chǔ)手冊17 —— 簡單語句
Python基礎(chǔ)手冊18 —— 復(fù)合語句(流程控制語句)
Python基礎(chǔ)手冊19 —— 迭代器
Python基礎(chǔ)手冊20 —— 生成器
Python基礎(chǔ)手冊21 —— 函數(shù)的定義
Python基礎(chǔ)手冊22 —— 函數(shù)的參數(shù)
Python基礎(chǔ)手冊23 —— 函數(shù)的調(diào)用
Python基礎(chǔ)手冊24 —— 函數(shù)中變量的作用域
Python基礎(chǔ)手冊25 —— 裝飾器
Python基礎(chǔ)手冊26 —— 錯誤 & 異常
Python基礎(chǔ)手冊27 —— 模塊
Python基礎(chǔ)手冊28 —— 模塊的高級概念
Python基礎(chǔ)手冊29 —— 包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市领斥,隨后出現(xiàn)的幾起案子嫉到,更是在濱河造成了極大的恐慌,老刑警劉巖月洛,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件何恶,死亡現(xiàn)場離奇詭異,居然都是意外死亡嚼黔,警方通過查閱死者的電腦和手機(jī)细层,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隔崎,“玉大人今艺,你說我怎么就攤上這事【糇洌” “怎么了虚缎?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钓株。 經(jīng)常有香客問我实牡,道長,這世上最難降的妖魔是什么轴合? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任创坞,我火速辦了婚禮,結(jié)果婚禮上受葛,老公的妹妹穿的比我還像新娘题涨。我一直安慰自己,他們只是感情好总滩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布纲堵。 她就那樣靜靜地躺著,像睡著了一般闰渔。 火紅的嫁衣襯著肌膚如雪席函。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天冈涧,我揣著相機(jī)與錄音茂附,去河邊找鬼正蛙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛营曼,可吹牛的內(nèi)容都是我干的乒验。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼溶推,長吁一口氣:“原來是場噩夢啊……” “哼徊件!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒜危,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤虱痕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后辐赞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體部翘,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年响委,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了新思。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赘风,死狀恐怖夹囚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情邀窃,我是刑警寧澤荸哟,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站瞬捕,受9級特大地震影響鞍历,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肪虎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一劣砍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扇救,春花似錦刑枝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至钾挟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饱岸,已是汗流浹背掺出。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工徽千, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汤锨。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓双抽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闲礼。 傳聞我的和親對象是個(gè)殘疾皇子牍汹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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