玩轉(zhuǎn)Python裝飾器

1.定義

裝飾器模式是面向?qū)ο笳Z(yǔ)言中經(jīng)典的設(shè)計(jì)模式之一背传,它的出現(xiàn)是為了解決在多個(gè)函數(shù)中添加某一統(tǒng)一的功能,從而減少代碼的重用。例如常應(yīng)用的場(chǎng)景:插入日志,計(jì)算性能撩扒,緩存運(yùn)算結(jié)果,事務(wù)處理等吨些。這里就來(lái)了解python中的裝飾器搓谆。

2.實(shí)例分析

首先看個(gè)經(jīng)典的算法題:一個(gè)共有10個(gè)臺(tái)階的樓梯,從下面走到上面豪墅,一次只能邁1-3個(gè)臺(tái)階泉手,并且不能后退,走完這個(gè)樓梯有多少種走法偶器?
首先這個(gè)題目就是斐波那契數(shù)列的一個(gè)延伸斩萌,無(wú)非就是一個(gè)遞歸問(wèn)題,當(dāng)然,這里重點(diǎn)是裝飾器
這里我們來(lái)看看代碼:

def climbfloor(n,steps):
   count=0
    if n == 0:
        count=1
    elif n > 0:
        for step in steps:
            count+=climbfloor(n-step,steps)
    return count
    print(climbfloor(10,(1,2,3))

運(yùn)行后我們很快就能得出結(jié)果屏轰,但是如果是爬上100颊郎、200層樓得出來(lái)的運(yùn)算


這里寫圖片描述

結(jié)果就非常大了,那是不是每次都要重新計(jì)算霎苗?當(dāng)然姆吭,現(xiàn)在的機(jī)器計(jì)算速度的很快了,但是如果在多任務(wù)處理的情況下唁盏,每次重新計(jì)算就大大的浪費(fèi)了資源内狸,影響用戶的體驗(yàn)检眯。于是我們可以考慮將運(yùn)算結(jié)果加入緩存,這樣下次運(yùn)算就能直接使用已經(jīng)計(jì)算的結(jié)果昆淡,所以我們來(lái)定義另一個(gè)函數(shù)锰瘸,

def memo(func):
    cache = {}
    def wrap(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrap

這個(gè)函數(shù)傳入一個(gè)func(函數(shù))參數(shù),加入一個(gè)包裹函數(shù)wrap()昂灵,在包裹函數(shù)里面添加緩存功能避凝,并調(diào)用func函數(shù)。這樣就達(dá)到了緩存計(jì)算結(jié)果的效果眨补,然后在打印結(jié)果前把需要進(jìn)行計(jì)算的函數(shù)傳入memo()函數(shù):
climbfloor = memo(climbfloor)
或者直接在原函數(shù)上面加入@memo關(guān)鍵字恕曲,這實(shí)際上是上面那句代碼的語(yǔ)法糖,二者是等價(jià)的渤涌。

這里寫圖片描述

這樣就把需要裝飾的函數(shù)當(dāng)作參數(shù)傳入了裝飾器函數(shù)佩谣,以后只要用到此類計(jì)算都可以直接傳入裝飾器,就可以為函數(shù)自動(dòng)寫入緩存功能实蓬。

3.定義帶參數(shù)的裝飾器

這里我們實(shí)現(xiàn)一個(gè)裝飾器茸俭,用來(lái)檢查被裝飾函數(shù)的參數(shù)類型,裝飾器可以通過(guò)參數(shù)指明函數(shù)參數(shù)的類型安皱,并且調(diào)用時(shí)如果檢測(cè)出類型不匹配就拋出一個(gè)異常调鬓,直接看截圖,重要注釋都標(biāo)明了酌伊。


這里寫圖片描述

這樣再隨便寫段測(cè)試代碼

@typeassert(int,str,list)
def f(a,b,c):
    print(a,b,c)
    
f(1,'abc',[1,2,3])
f(1,2,[1,2,3])

點(diǎn)擊運(yùn)行


這里寫圖片描述

可以看到成功的進(jìn)行了參數(shù)類型的檢查 這樣函數(shù)帶參數(shù)的函數(shù)裝飾器就完成了

4.實(shí)現(xiàn)屬性可修改的函數(shù)裝飾器

現(xiàn)在需要用裝飾器計(jì)算一個(gè)函數(shù)的運(yùn)行時(shí)間腾窝,設(shè)置一個(gè)timeout值,如果函數(shù)運(yùn)行時(shí)間超過(guò)timeout居砖,就在控制臺(tái)打印出相關(guān)信息虹脯。這里我們來(lái)看看具體代碼

def warn(timeout):
    def decorator(func):
        def wrapper(*args,**kargs):
            start = time.time()
            # 傳入函數(shù)參數(shù)
            res = func(*args,**kargs)
            # 計(jì)算函數(shù)運(yùn)行所需的時(shí)間
            used = time.time() - start
            if used > timeout:
                msg = '"%s": %s > %s'%(func.__name__,used,timeout)
                # 打印msg信息
                logging.warning(msg)
            return res
        return wrapper
    return decorator

這樣就可以直接在對(duì)任意的函數(shù)使用了,現(xiàn)在有一個(gè)問(wèn)題奏候,如果希望在函數(shù)運(yùn)行時(shí)動(dòng)態(tài)改變timeout的值循集,應(yīng)該怎樣做?很簡(jiǎn)單蔗草,只要在裝飾器里面再定義一個(gè)函數(shù)setTimeout(),就可以解決問(wèn)題了咒彤。但是可以看到,運(yùn)行時(shí)間的判斷是在wrapper()中的咒精,是一個(gè)閉包镶柱,我們修改timeout的值應(yīng)該怎樣傳遞到閉包當(dāng)中?這里就需要用到nonlocal關(guān)鍵字模叙,用它來(lái)聲明變量歇拆,不是只在當(dāng)前函數(shù)中有效,而是能作用到整個(gè)裝飾器函數(shù)中。這樣就直接在decorator()中直接定義一個(gè)setTimeout()函數(shù)

        def setTimeout(k):
            nonlocal timeout
            timeout = k
        wrapper.setTimeout = setTimeout

注意這個(gè)函數(shù)是放在wrapper返回之前的查吊,不然就起不到作用了。
最后進(jìn)行裝飾器的測(cè)試


@warn(1.5)
def test():
    print("In sert")
    while randint(0,1):
        time.sleep(0.5)

for _ in range(30):
    test()
test.setTimeout(1)
for _ in range(30):
    test()

運(yùn)行后再觀察控制臺(tái)信息


這里寫圖片描述

這樣就實(shí)現(xiàn)了函數(shù)裝飾器屬性的修改湖蜕。

到這里逻卖,相信你應(yīng)該能更順手的使用Python的裝飾器了。

最后本文示例均來(lái)自慕課網(wǎng)實(shí)戰(zhàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昭抒,一起剝皮案震驚了整個(gè)濱河市评也,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灭返,老刑警劉巖盗迟,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異熙含,居然都是意外死亡罚缕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門怎静,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)邮弹,“玉大人,你說(shuō)我怎么就攤上這事蚓聘‰缦纾” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵夜牡,是天一觀的道長(zhǎng)与纽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)塘装,這世上最難降的妖魔是什么急迂? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮蹦肴,結(jié)果婚禮上袋毙,老公的妹妹穿的比我還像新娘。我一直安慰自己冗尤,他們只是感情好听盖,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著裂七,像睡著了一般皆看。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上背零,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天腰吟,我揣著相機(jī)與錄音,去河邊找鬼。 笑死毛雇,一個(gè)胖子當(dāng)著我的面吹牛嫉称,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灵疮,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼织阅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了震捣?” 一聲冷哼從身側(cè)響起荔棉,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒿赢,沒(méi)想到半個(gè)月后润樱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡羡棵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年壹若,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皂冰。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舌稀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出灼擂,到底是詐尸還是另有隱情壁查,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布剔应,位于F島的核電站睡腿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏峻贮。R本人自食惡果不足惜席怪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纤控。 院中可真熱鬧挂捻,春花似錦、人聲如沸船万。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)耿导。三九已至声怔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舱呻,已是汗流浹背醋火。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芥驳。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓柿冲,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親兆旬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子假抄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)爵憎,斷路器慨亲,智...
    卡卡羅2017閱讀 134,701評(píng)論 18 139
  • 7.1 創(chuàng)建裝飾器 與java中的裝飾器模式類似婚瓜,其作用就是將一些多余的宝鼓、能夠重復(fù)使用的代碼抽離出來(lái),然后通過(guò)py...
    Lemon_Home閱讀 506評(píng)論 0 0
  • 本文為《爬著學(xué)Python》系列第四篇文章巴刻。從本篇開始愚铡,本專欄在順序更新的基礎(chǔ)上,會(huì)有不規(guī)則的更新胡陪。 在Pytho...
    SyPy閱讀 2,505評(píng)論 4 11
  • 此開卷第一回也沥寥。作者自云曾歷過(guò)一番夢(mèng)幻之后,故將真事隱去柠座,而借“通靈”撰此《石頭記》一書也邑雅,故曰“甄士隱”...
    夏雨慕雪閱讀 788評(píng)論 0 0
  • 俗話說(shuō)得好淮野,百樣懂不如精一門。尤其在如今信息越發(fā)發(fā)達(dá)吹泡,行業(yè)越發(fā)細(xì)分的時(shí)年代骤星,深耕一門,擁有一門獨(dú)門絕活爆哑,更顯得尤為...
    善思而行閱讀 1,380評(píng)論 0 0