Python進(jìn)階話(huà)題雜談(十四)裝飾器

裝飾器是Python中最難理解的語(yǔ)法之一静浴,但較之其他冷門(mén)語(yǔ)法又相對(duì)較常用堰氓。但有必要指出的是,這里所說(shuō)的較常用马绝,指的是Python自帶的一些裝飾器豆赏,如@property、@abstractmethod等富稻,自定義裝飾器在實(shí)際中是較少使用的掷邦。

1 高階函數(shù)

函數(shù),作為一段代碼的抽象椭赋,其本質(zhì)上也對(duì)應(yīng)于一個(gè)表示函數(shù)代碼起點(diǎn)的內(nèi)存地址抚岗。每當(dāng)調(diào)用一個(gè)函數(shù)時(shí),程序掛起并跳轉(zhuǎn)到函數(shù)所在的地址開(kāi)始執(zhí)行哪怔,結(jié)束后再退回到調(diào)用點(diǎn)宣蔚,繼續(xù)執(zhí)行接下來(lái)的語(yǔ)句。由此可見(jiàn)认境,就數(shù)據(jù)結(jié)構(gòu)而言胚委,函數(shù)與數(shù)組的原理類(lèi)似,可以用一個(gè)指針來(lái)保存函數(shù)的起始位置叉信。故在C語(yǔ)言中有“函數(shù)指針”類(lèi)型亩冬,且由于函數(shù)可以用指針來(lái)傳遞,函數(shù)指針就可以作為另一個(gè)函數(shù)的參數(shù)硼身,從而出現(xiàn)了“以函數(shù)作為參數(shù)的函數(shù)”硅急,這樣的函數(shù)就稱(chēng)為高階函數(shù)。

在Python中佳遂,由于對(duì)象引用的存在营袜,函數(shù)也同樣作為一類(lèi)對(duì)象(函數(shù)對(duì)象),可以通過(guò)其引用傳遞丑罪,故在Python中將函數(shù)作為參數(shù)傳遞荚板,與其他數(shù)據(jù)結(jié)構(gòu)的傳遞是一樣的凤壁。Python中就有一些常用的高階函數(shù),如sort啸驯、max客扎、min等帶有key參數(shù)的函數(shù),map罚斗、reduce、filter這樣的帶有函數(shù)參數(shù)的函數(shù)等等宅楞。這樣的函數(shù)都以函數(shù)作為參數(shù)针姿,并在函數(shù)內(nèi)部調(diào)用傳入的函數(shù)參數(shù)。

2 無(wú)參裝飾器

Python的裝飾器被定義為一個(gè)高階函數(shù)厌衙,這個(gè)高階函數(shù)接受一個(gè)函數(shù)作為其唯一函數(shù)距淫,并返回另一個(gè)函數(shù)作為其唯一返回值,這樣的特殊函數(shù)就稱(chēng)為裝飾器婶希。裝飾器函數(shù)內(nèi)部應(yīng)包含一個(gè)完整的函數(shù)定義過(guò)程榕暇,并將這個(gè)新的函數(shù)作為返回值返回。在此函數(shù)內(nèi)部應(yīng)包含原函數(shù)的調(diào)用語(yǔ)句喻杈,以及其他額外添加的語(yǔ)句彤枢。裝飾器定義完成后,就可以使用“@”符號(hào)對(duì)其他函數(shù)進(jìn)行裝飾筒饰。

當(dāng)使用裝飾器時(shí)缴啡,本質(zhì)上是進(jìn)行了如下過(guò)程:

@staticmethod

def xxx():

???pass

等同于:

def xxx():

???pass

xxx = staticmethod(xxx)

可見(jiàn),裝飾器語(yǔ)法等同于使用裝飾器函數(shù)處理原函數(shù)瓷们,并賦值回原函數(shù)的過(guò)程业栅。

對(duì)于裝飾器內(nèi)部定義的新函數(shù),其作為返回值返回后谬晕,應(yīng)保持與原函數(shù)一致的函數(shù)參數(shù)聲明碘裕,這樣才能保證參數(shù)正確傳遞。而裝飾器內(nèi)部定義的函數(shù)由于具有通用性攒钳,是不能像普通函數(shù)一樣定義有限多個(gè)形參的帮孔,故這個(gè)問(wèn)題的解決方案為:使用不定長(zhǎng)形參聲明,并在函數(shù)調(diào)用時(shí)使用參數(shù)解包夕玩。

下例定義了一個(gè)常見(jiàn)的計(jì)時(shí)用裝飾器:

import time

import numpy as np

def Timer(func):

???def newFunc(*args, **kwargs):

???????sTime = time.time()

???????returnTuple = func(*args, **kwargs)

???????eTime = time.time()

???????print('Time: %.6f' % (eTime - sTime))

???????return returnTuple

???return newFunc

@Timer

def Test(timesNum):

???for i in range(timesNum):

???????np.random.rand(100, 100).dot(np.random.rand(100, 100))

Test(10)

上述代碼定義了一個(gè)名為T(mén)imer的裝飾器函數(shù)你弦,這個(gè)函數(shù)接受一個(gè)函數(shù)作為參數(shù),并在內(nèi)部定義了一個(gè)聲明為def newFunc(*args, **kwargs)的通用函數(shù)燎孟,在這個(gè)函數(shù)內(nèi)部禽作,以解包參數(shù)*args, **kwargs調(diào)用了裝飾器傳入的函數(shù),并在調(diào)用前后保存了調(diào)用時(shí)間揩页。最終輸出消耗時(shí)間旷偿,并返回函數(shù)的返回值。上述這個(gè)函數(shù)定義,最終作為裝飾器函數(shù)的返回值返回萍程。在裝飾器外部幢妄,這個(gè)返回的函數(shù)將覆蓋原函數(shù)。所以茫负,在調(diào)用被Timer裝飾的Test函數(shù)時(shí)蕉鸳,函數(shù)不僅會(huì)執(zhí)行Test函數(shù)的內(nèi)容,還會(huì)執(zhí)行Timer裝飾器中所定義的附加內(nèi)容忍法,即雖然調(diào)用的是Test函數(shù)潮尝,但實(shí)際執(zhí)行的是以Test函數(shù)作為參數(shù)的newFunc函數(shù)七咧。

3 有參裝飾器

有參裝飾器是“返回裝飾器函數(shù)的函數(shù)”茄蚯,本人目前并不了解這種語(yǔ)法的具體應(yīng)用場(chǎng)景均蜜,故這里只對(duì)有參裝飾器做簡(jiǎn)單的語(yǔ)法上的討論糠悼。

仍然以上文中的計(jì)時(shí)函數(shù)為例闲勺,有參裝飾器可以使用參數(shù)修改裝飾器的行為誊役,如定義時(shí)間縮放:

import time

import numpy as np

def Timer(mulNum):

???def innerTimer(func):

???????def newFunc(*args, **kwargs):

???????????sTime = time.time()

???????????returnTuple = func(*args, **kwargs)

???????????eTime = time.time()

???????????print('Time: %.6f' % ((eTime - sTime) * mulNum))

???????????return returnTuple

???????return newFunc

???return innerTimer

@Timer(1000)

def Test(timesNum):

???for i in range(timesNum):

???????np.random.rand(100, 100).dot(np.random.rand(100, 100))

Test(10)

上述代碼中婿脸,Timer是一個(gè)有參裝飾器础倍,其參數(shù)用于定義時(shí)間縮放值咽弦。Timer應(yīng)返回一個(gè)裝飾器徒蟆,故整個(gè)裝飾器的定義被放在了Timer內(nèi)部。而innerTimer為裝飾器函數(shù)离唬,其最終被Timer返回后专。innerTimer應(yīng)接受一個(gè)函數(shù)作為參數(shù),并返回一個(gè)新的函數(shù)输莺,故在innerTimer內(nèi)部定義了一個(gè)新的函數(shù)作為返回值戚哎,這個(gè)函數(shù)的定義中包含了對(duì)傳入的函數(shù)參數(shù)func的調(diào)用,同時(shí)包含了計(jì)時(shí)語(yǔ)句嫂用,并最終利用時(shí)間型凳,以及有參裝飾器的參數(shù)mulNum共同計(jì)算輸出值。innerTimer裝飾器函數(shù)返回在其內(nèi)部定義的函數(shù)newFunc嘱函,而Timer裝飾器函數(shù)返回innerTimer裝飾器函數(shù)甘畅,并最終將此裝飾器交給被裝飾的原函數(shù)。

綜上往弓,有參裝飾器本質(zhì)上是一個(gè)“返回(無(wú)參)裝飾器函數(shù)的任意函數(shù)”疏唾,而無(wú)參裝飾器函數(shù)是一個(gè)“接受函數(shù)作為唯一參數(shù),并返回一個(gè)新的函數(shù)的函數(shù)”函似。有參裝飾器只要求返回值是一個(gè)無(wú)參裝飾器即可槐脏,而無(wú)參裝飾器函數(shù)將修飾被其裝飾的原函數(shù),從而將原函數(shù)覆蓋為一個(gè)裝飾后的新函數(shù)撇寞。從而使得后續(xù)代碼中所有對(duì)原函數(shù)的調(diào)用顿天,實(shí)際調(diào)用的都是修飾后的新函數(shù)堂氯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市牌废,隨后出現(xiàn)的幾起案子咽白,更是在濱河造成了極大的恐慌,老刑警劉巖鸟缕,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晶框,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡叁扫,警方通過(guò)查閱死者的電腦和手機(jī)三妈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)莫绣,“玉大人,你說(shuō)我怎么就攤上這事悠鞍《允遥” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵咖祭,是天一觀的道長(zhǎng)掩宜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)么翰,這世上最難降的妖魔是什么牺汤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮浩嫌,結(jié)果婚禮上檐迟,老公的妹妹穿的比我還像新娘。我一直安慰自己码耐,他們只是感情好追迟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著骚腥,像睡著了一般敦间。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上束铭,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天廓块,我揣著相機(jī)與錄音,去河邊找鬼契沫。 笑死带猴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的埠褪。 我是一名探鬼主播浓利,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挤庇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了贷掖?” 一聲冷哼從身側(cè)響起嫡秕,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苹威,沒(méi)想到半個(gè)月后昆咽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牙甫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年掷酗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窟哺。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泻轰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出且轨,到底是詐尸還是另有隱情浮声,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布旋奢,位于F島的核電站泳挥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏至朗。R本人自食惡果不足惜屉符,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锹引。 院中可真熱鬧矗钟,春花似錦、人聲如沸粤蝎。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)初澎。三九已至秸应,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碑宴,已是汗流浹背软啼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留延柠,地道東北人祸挪。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像贞间,于是被迫代替她去往敵國(guó)和親贿条。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雹仿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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

  • 〇、前言 本文共108張圖整以,流量黨請(qǐng)慎重胧辽! 歷時(shí)1個(gè)半月,我把自己學(xué)習(xí)Python基礎(chǔ)知識(shí)的框架詳細(xì)梳理了一遍公黑。 ...
    Raxxie閱讀 18,954評(píng)論 17 410
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理邑商,服務(wù)發(fā)現(xiàn),斷路器凡蚜,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • 9837fd914689閱讀 132評(píng)論 1 1
  • 2016年的春節(jié)假期終于過(guò)去了人断。丙申年開(kāi)始了。猴年朝蜘。C記FY16上半年的業(yè)績(jī)不錯(cuò)恶迈。大家的心情也不錯(cuò)。今年谱醇,ACI有...
    taoza閱讀 202評(píng)論 0 0