理解和使用Python裝飾器

裝飾器在 Python 中無處不在北滥,功能強(qiáng)大。本篇介紹裝飾器的原理和用法闸翅,力求通俗易懂再芋。

我們從一個(gè)簡單的例子開始,逐步展開坚冀。假設(shè)有一個(gè)函數(shù)济赎,函數(shù)隨便做點(diǎn)啥:

def foo():
    print("do something.")

foobar 在英語中相當(dāng)于漢語的張三、李四,意思就是隨便給個(gè)名字∷狙担現(xiàn)在要給這個(gè)函數(shù)增加點(diǎn)功能华蜒,比如在函數(shù)調(diào)用前和調(diào)用后都打印一個(gè)輸出提示。我們?nèi)粘i_發(fā)中經(jīng)常有這類橫向插入的需求豁遭,比如日志、權(quán)限檢查等等贺拣。在 Java 中蓖谢,實(shí)現(xiàn)這個(gè)功能要用大代理模式,但在 Python 中卻異常簡單譬涡,我們只需要另外一個(gè)嵌套的函數(shù)闪幽,如下:

def outer(func):       
    def inner():
        print("before execution.")
        func()
        print("after execution.")

    return inner # 無括號(hào)

然后這樣使用:

if __name__ == "__main__":    
    proxy = outer(foo)
    proxy()

程序輸出:

before execution.
do something.
after execution.

在 Python 中,一切都是對(duì)象涡匀,函數(shù)也是對(duì)象盯腌。所以

proxy = outer(foo)

就是把 foo 函數(shù)作為參數(shù)傳給 outer 函數(shù),而 outer 函數(shù)呢陨瘩,返回值為 inner 函數(shù)腕够,所以 proxy 變量也參照inner 函數(shù),再執(zhí)行 proxy() 語句舌劳,就是調(diào)用 inner 函數(shù)帚湘,我們看內(nèi)函數(shù) inner 的代碼,一共有三個(gè)語句:

print("before execution.")      # 打印輸出
func()                          # 執(zhí)行 foo() 函數(shù)
print("after execution.")       # 打印輸出

Python 提供了裝飾器語法糖·(@decorater_name)甚淡,讓我們使用這種嵌套的函數(shù)更加簡單大诸。下面是變更后的代碼:

def outer(func):       
    def inner():
        # 代碼同上,省略
    return inner 

@outer
def foo():
    print("do something.")

if __name__ == "__main__":    
    foo()

@outer 是一個(gè)語法糖贯卦,也就是我們所說的裝飾器资柔,這個(gè) @outer 裝飾器就是告訴 Python,在調(diào)用 foo 函數(shù)的時(shí)候撵割,把它傳給 outer 函數(shù)作為參數(shù)贿堰。outer 函數(shù)通過上述機(jī)制,既保證調(diào)用 foo 函數(shù)睁枕,也通過它自己的代碼增強(qiáng)了 foo 的功能官边。

上述代碼為了說明機(jī)制,代碼極其簡化外遇,一般化的裝飾器代碼是這樣的:inner 函數(shù)有兩個(gè)參數(shù)注簿,以增加其靈活性。假設(shè)我們的需求實(shí)現(xiàn)一個(gè) logger 裝飾器跳仿,記錄函數(shù)被調(diào)用的時(shí)間:

from datetime import datetime

def logger(func):
    def wrapper(*args, **kwargs):
        print ('[INFO] {}, function "{}" was called '.format(datetime.now(), func.__name__))
        return func(*args, **kwargs)
    return wrapper

@logger
def foo():
    print("do something.")

if __name__ == "__main__":    
    foo()

因?yàn)檠b飾器器外部函數(shù)需要以 function 作為參數(shù)诡渴,所以如果調(diào)用函數(shù)需要有信息需要傳給裝飾器,就需要再增加一層嵌套。

from datetime import datetime

def logger(msg):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print('[INFO] {}, "{}" was called with message "{}"'.format(
                datetime.now(), func.__name__, msg))
            return func(*args, **kwargs)
        return wrapper
    return decorator

@logger("Maybe bored.")
def foo(name):
    print("do something, " + name)

foo('Johnny')

裝飾器也可以基于類來實(shí)現(xiàn)妄辩,要求裝飾器類實(shí)現(xiàn) __init__() 方法和 __call__() 方法惑灵。類裝飾器實(shí)現(xiàn) logger 的代碼如下:

from datetime import datetime

class logger(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print ('[INFO] {}, function "{}" was called '.format(
            datetime.now(), self.func.__name__))

        return self.func(*args, **kwargs)

@logger
def foo():
    print("do something.")

if __name__ == "__main__":    
    foo()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市眼耀,隨后出現(xiàn)的幾起案子英支,更是在濱河造成了極大的恐慌,老刑警劉巖哮伟,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件干花,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡楞黄,警方通過查閱死者的電腦和手機(jī)池凄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鬼廓,“玉大人肿仑,你說我怎么就攤上這事∷樗埃” “怎么了尤慰?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蚣录。 經(jīng)常有香客問我割择,道長,這世上最難降的妖魔是什么萎河? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任荔泳,我火速辦了婚禮,結(jié)果婚禮上虐杯,老公的妹妹穿的比我還像新娘玛歌。我一直安慰自己,他們只是感情好擎椰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布支子。 她就那樣靜靜地躺著,像睡著了一般达舒。 火紅的嫁衣襯著肌膚如雪值朋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天巩搏,我揣著相機(jī)與錄音昨登,去河邊找鬼。 笑死贯底,一個(gè)胖子當(dāng)著我的面吹牛丰辣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼笙什,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼飘哨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起琐凭,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤芽隆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后统屈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摆马,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年鸿吆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片述呐。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惩淳,死狀恐怖稚补,靈堂內(nèi)的尸體忽然破棺而出提陶,到底是詐尸還是另有隱情惧互,我是刑警寧澤为流,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布扼雏,位于F島的核電站岂傲,受9級(jí)特大地震影響扫沼,放射性物質(zhì)發(fā)生泄漏叁温。R本人自食惡果不足惜江掩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一学辱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧环形,春花似錦策泣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至火本,卻和暖如春危队,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钙畔。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國打工茫陆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刃鳄。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓盅弛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挪鹏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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

  • 部分細(xì)節(jié)自己改了點(diǎn)见秽,也加了點(diǎn)自己例子,基本上屬于轉(zhuǎn)載讨盒。轉(zhuǎn)載出處:https://my.oschina.net/le...
    洛克黃瓜閱讀 1,976評(píng)論 0 3
  • 呵呵解取!作為一名教python的老師,我發(fā)現(xiàn)學(xué)生們基本上一開始很難搞定python的裝飾器返顺,也許因?yàn)檠b飾器確實(shí)很難懂...
    TypingQuietly閱讀 19,554評(píng)論 26 186
  • Python的裝飾器的英文名叫Decorator禀苦,要對(duì)一個(gè)已有的模塊做一些“修飾工作”,所謂修飾工作就是想給現(xiàn)有的...
    Spareribs閱讀 676評(píng)論 1 11
  • 正在閱讀的書:《人生的84000種可能》 閱讀有效時(shí)間:30分鐘 閱讀中遇到的困難:筆記較為繁瑣遂鹊,需要學(xué)習(xí)思維導(dǎo)圖...
    李聘2023閱讀 101評(píng)論 0 0
  • 第一次為教育感到悲哀振乏,它來自學(xué)生對(duì)副科的輕視,而代課的日子便是開始…… 2019年3月秉扑,我來到四川的一...
    桓朗閱讀 194評(píng)論 0 2