理解Python裝飾器(Decorator)

理解Python裝飾器(Decorator)

Python裝飾器看起來(lái)類似Java中的注解丐膝,然鵝和注解并不相同,不過(guò)同樣能夠?qū)崿F(xiàn)面向切面編程踢京。

想要理解Python中的裝飾器跨算,不得不先理解閉包(closure)這一概念轻要。

閉包

看看維基百科中的解釋:

在計(jì)算機(jī)科學(xué)中,閉包(英語(yǔ):Closure)旱爆,又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures)舀射,是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在怀伦,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外脆烟。

官方的解釋總是不說(shuō)人話,but--talk is cheap房待,show me the code:

# print_msg是外圍函數(shù)
def print_msg():
    msg = "I'm closure"

    # printer是嵌套函數(shù)
    def printer():
        print(msg)

    return printer


# 這里獲得的就是一個(gè)閉包
closure = print_msg()
# 輸出 I'm closure
closure()

msg是一個(gè)局部變量邢羔,在print_msg函數(shù)執(zhí)行之后應(yīng)該就不會(huì)存在了。但是嵌套函數(shù)引用了這個(gè)變量桑孩,將這個(gè)局部變量封閉在了嵌套函數(shù)中拜鹤,這樣就形成了一個(gè)閉包。

結(jié)合這個(gè)例子再看維基百科的解釋流椒,就清晰明了多了敏簿。閉包就是引用了自有變量的函數(shù),這個(gè)函數(shù)保存了執(zhí)行的上下文,可以脫離原本的作用域獨(dú)立存在惯裕。

下面來(lái)看看Python中的裝飾器温数。

裝飾器

一個(gè)普通的裝飾器一般是這樣:

import functools


def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('call %s():' % func.__name__)
        print('args = {}'.format(*args))
        return func(*args, **kwargs)

    return wrapper

這樣就定義了一個(gè)打印出方法名及其參數(shù)的裝飾器。

調(diào)用之:

@log
def test(p):
    print(test.__name__ + " param: " + p)
    
test("I'm a param")

輸出:

call test():
args = I'm a param
test param: I'm a param

裝飾器在使用時(shí)蜻势,用了@語(yǔ)法撑刺,讓人有些困擾。其實(shí)握玛,裝飾器只是個(gè)方法够傍,與下面的調(diào)用方式?jīng)]有區(qū)別:

def test(p):
    print(test.__name__ + " param: " + p)

wrapper = log(test)
wrapper("I'm a param")

@語(yǔ)法只是將函數(shù)傳入裝飾器函數(shù),并無(wú)神奇之處败许。

值得注意的是@functools.wraps(func)王带,這是python提供的裝飾器。它能把原函數(shù)的元信息拷貝到裝飾器里面的 func 函數(shù)中市殷。函數(shù)的元信息包括docstring愕撰、name、參數(shù)列表等等醋寝「阏酰可以嘗試去除@functools.wraps(func),你會(huì)發(fā)現(xiàn)test.__name__的輸出變成了wrapper音羞。

帶參數(shù)的裝飾器

裝飾器允許傳入?yún)?shù)囱桨,一個(gè)攜帶了參數(shù)的裝飾器將有三層函數(shù),如下所示:

import functools

def log_with_param(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('call %s():' % func.__name__)
            print('args = {}'.format(*args))
            print('log_param = {}'.format(text))
            return func(*args, **kwargs)

        return wrapper

    return decorator
    
@log_with_param("param")
def test_with_param(p):
    print(test_with_param.__name__)

看到這個(gè)代碼是不是又有些疑問(wèn)嗅绰,內(nèi)層的decorator函數(shù)的參數(shù)func是怎么傳進(jìn)去的舍肠?和上面一般的裝飾器不大一樣啊。

其實(shí)道理是一樣的窘面,將其@語(yǔ)法去除翠语,恢復(fù)函數(shù)調(diào)用的形式一看就明白了:

# 傳入裝飾器的參數(shù),并接收返回的decorator函數(shù)
decorator = log_with_param("param")
# 傳入test_with_param函數(shù)
wrapper = decorator(test_with_param)
# 調(diào)用裝飾器函數(shù)
wrapper("I'm a param")

輸出結(jié)果與正常使用裝飾器相同:

call test_with_param():
args = I'm a param
log_param = param
test_with_param

至此财边,裝飾器這個(gè)有點(diǎn)費(fèi)解的特性也沒(méi)什么神秘了肌括。

裝飾器這一語(yǔ)法體現(xiàn)了Python中函數(shù)是第一公民,函數(shù)是對(duì)象酣难、是變量谍夭,可以作為參數(shù)、可以是返回值憨募,非常的靈活與強(qiáng)大紧索。

Python中引入了很多函數(shù)式編程的特性,需要好好學(xué)習(xí)與體會(huì)馋嗜。

最后編輯于
?著作權(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)離奇詭異济舆,居然都是意外死亡卿泽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門滋觉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)签夭,“玉大人,你說(shuō)我怎么就攤上這事椎侠〉谧猓” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵我纪,是天一觀的道長(zhǎng)慎宾。 經(jīng)常有香客問(wèn)我,道長(zhǎ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
  • 文/蒼蘭香墨 我猛地睜開眼召衔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼铃诬!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤趣席,失蹤者是張志新(化名)和其女友劉穎兵志,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宣肚,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡想罕,尸身上長(zhǎng)有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
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窑多。三九已至,卻和暖如春洼滚,著一層夾襖步出監(jiān)牢的瞬間埂息,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工遥巴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留千康,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓铲掐,卻偏偏與公主長(zhǎng)得像拾弃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(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
  • 每個(gè)人都有的內(nèi)褲主要功能是用來(lái)遮羞搭盾,但是到了冬天它沒(méi)法為我們防風(fēng)御寒咳秉,咋辦?我們想到的一個(gè)辦法就是把內(nèi)褲改造一下增蹭,...
    chen_000閱讀 1,364評(píng)論 0 3
  • Python 中的函數(shù)和 Java滴某、C++不太一樣,Python 中的函數(shù)可以像普通變量一樣當(dāng)做參數(shù)傳遞給另外一個(gè)...
    stoneyang94閱讀 1,343評(píng)論 1 4
  • 【第三篇】 壹 我倆是16年10月31日領(lǐng)的證幕侠,確定戀愛關(guān)系的時(shí)間是12年4月22日,那是大二下學(xué)期碍彭。 四年半的戀...
    劉之璋閱讀 431評(píng)論 0 0
  • 今天中午吃飯的空當(dāng)晤硕,看到了公司下小花園各種顏色交織的景色。讓我立馬想到一個(gè)詞兒庇忌,五顏六色舞箍。 在我的記憶里,能和五顏...
    昆侖的石頭閱讀 335評(píng)論 0 0