裝飾器

????????在定義函數之后期贫,希望擴展這些函數的功能梳星,譬如在函數調用前后自動打印日志赞赖,但如果是一些通用的功能,修改每一個函數又會顯得比較麻煩冤灾。最好的方法就是定義一個裝飾器前域,給每個函數增加功能。這種在代碼運行期間動態(tài)增加函數功能的方式韵吨,稱為裝飾器(Decorator)


原理

不帶參數的裝飾器

@a_decorator

def f(...):?

?????????...

#經過a_decorator后匿垄, 函數f就相當于以f為參數調用a_decorator返回結果

f = a_decorator(f)

來分析這個式子归粉, 可以看出至少要滿足以下幾個條件?

1. 裝飾器函數運行在函數定義的時候?

2. 裝飾器需要返回一個可執(zhí)行的對象?

3. 裝飾器返回的可執(zhí)行對象要兼容函數f的參數


初始函數

from datetime import datetime

def now():?

? ? ????print(datetime.now())?

>>>now()


增加功能

def now():?

????????print('run %s().' % now.__name__)

? ? ? ? print(datetime.now())

? ? ? ? print('run %s() finished.' % now.__name__)

>>>now()


def decorator(fun): ? ? ? ?#將函數作為參數傳入

????????def wrapper(*args,**kw):?

????????????????print('run %s().' % fun.__name__)

? ? ? ? ? ? ????outcome = fun(*args,**kw)

? ? ? ? ? ? ????print('run %s() finished.' % fun.__name__)

? ? ? ? ? ? ????return outcome ? ?#將函數作為返回值返回

? ? ? ? return wrapper

now = decorator(now)

now()

????????這是裝飾器最直觀的表示了椿疗,定義一個裝飾器函數,然后將自己的函數傳入糠悼,輸出的新函數即添加了新功能届榄。本質上來看,裝飾器就是一個高階函數倔喂。由于其接收參數為(*args,**kw),所以能夠接受任何形式的調用铝条。在wrapper函數內,調用傳入的fun()函數之外席噩,就可以定義新的功能班缰。


__name__問題

>>>now()

run now().

2016-05-31 17:10:32.217998

run now() finished.

>>>now.__name__

'wrapper' ? ? ? #在這里名字應該為now,被改變了

????????調用完成之后班挖,新的問題又出現(xiàn)了:函數的__name__屬性因為使用裝飾器改變了鲁捏,有些依賴函數簽名的代碼執(zhí)行就會出錯。


python內置的functools.wraps能夠將函數名稱替換回來萧芙。一個完整的decorator寫法如下:

>>> import functools

>>> def decorator(fun):?

?????????????????@functools.wraps(fun)?

?????????????????def wrapper(*args,**kw):?

?????????????????????????print('run %s().' % fun.__name__)

? ? ? ? ? ? ? ? ? ? ? ? ?outcome = fun(*args,**kw)

? ? ? ? ? ? ? ? ? ? ? ? ?print('run %s() finished.' % fun.__name__)

? ? ? ? ? ? ????????????return outcome

? ? ? ????????? return wrapper

>>> @decorator?

?????????def now():?

?????????????????print(datetime.now())

>>> now()

run now().2016-05-31 17:32:59.270645

run now() finished.

>>> now.__name__

'now'


那么给梅,問題來了,我們能不能創(chuàng)建一個裝飾器双揪,能夠傳入參數动羽,也可以不傳入參數呢?

這需要對裝飾器的運行過程有充分的了解渔期,觀察上面的兩類裝飾器运吓,主要的區(qū)別在于:

沒有參數的裝飾器渴邦,最外層接收的是func函數,最終返回的函數是wrapper(*args,**kw)拘哨,直接接收func函數參數

帶參數的裝飾器谋梭,最外層接收的是參數,而最終返回的函數是wrapper(func)倦青,接收func函數

那么瓮床,我們就可以通過判定最外層的函數接收的是func函數還是參數,來決定是有參數或者是無參數的裝飾器产镐,最終決定如何返回隘庄。嘗試結果如下:



裝飾器類

????????在Python中, 其實函數也是對象癣亚。 反過來丑掺, 對象其實也可以像函數一樣調用, 只要在類的方法中實現(xiàn)__call__()方法。

????????__new__對象的創(chuàng)建述雾,是一個靜態(tài)方法街州,第一個參數是cls。(想想也是绰咽,不可能是self菇肃,對象還沒創(chuàng)建地粪,哪來的self)

(__new__方法在類定義中不是必須寫的取募,如果沒定義,默認會調用object.__new__去創(chuàng)建一個對象蟆技。如果定義了玩敏,就是override,可以custom創(chuàng)建對象的行為。聰明的讀者可能想到质礼,既然__new__可以custom對象的創(chuàng)建旺聚,那我在這里做一下手腳,每次創(chuàng)建對象都返回同一個眶蕉,那不就是單例模式了嗎)

__init__ : 對象的初始化砰粹, 是一個實例方法,第一個參數是self造挽。

(__init__其實不是實例化一個類的時候第一個被調用的方法碱璃。當使用 Persion(name, age) 這樣的表達式來實例化一個類時,最先被調用的方法 其實是 __new__ 方法饭入。)

__call__ : 對象可call嵌器,注意不是類,是對象

(對象通過提供__call__(slef, [,*args [,**kwargs]])方法可以模擬函數的行為谐丢,如果一個對象x提供了該方法爽航,就可以像函數一樣使用它蚓让,也就是說x(arg1, arg2...) 等同于調用x.__call__(self, arg1, arg2) 。模擬函數的對象可以用于創(chuàng)建防函數(functor) 或代理(proxy).)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末讥珍,一起剝皮案震驚了整個濱河市历极,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衷佃,老刑警劉巖执解,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異纲酗,居然都是意外死亡衰腌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門觅赊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來右蕊,“玉大人,你說我怎么就攤上這事吮螺∪那簦” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵鸠补,是天一觀的道長萝风。 經常有香客問我,道長紫岩,這世上最難降的妖魔是什么规惰? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮泉蝌,結果婚禮上歇万,老公的妹妹穿的比我還像新娘。我一直安慰自己勋陪,他們只是感情好贪磺,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诅愚,像睡著了一般寒锚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上违孝,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天刹前,我揣著相機與錄音,去河邊找鬼等浊。 笑死腮郊,一個胖子當著我的面吹牛,可吹牛的內容都是我干的筹燕。 我是一名探鬼主播轧飞,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼衅鹿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了过咬?” 一聲冷哼從身側響起大渤,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掸绞,沒想到半個月后泵三,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡衔掸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年烫幕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敞映。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡较曼,死狀恐怖,靈堂內的尸體忽然破棺而出振愿,到底是詐尸還是另有隱情捷犹,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布冕末,位于F島的核電站萍歉,受9級特大地震影響,放射性物質發(fā)生泄漏档桃。R本人自食惡果不足惜枪孩,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胳蛮。 院中可真熱鬧销凑,春花似錦丛晌、人聲如沸仅炊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抚垄。三九已至,卻和暖如春谋逻,著一層夾襖步出監(jiān)牢的瞬間呆馁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工毁兆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留浙滤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓气堕,卻偏偏與公主長得像纺腊,于是被迫代替她去往敵國和親畔咧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內容

  • 裝飾是為函數和類指定管理代碼的一種方式.裝飾器本身的形式是處理其他的可調用對象的可調用的對象揖膜。 函數裝飾器在函數定...
    低吟淺唱1990閱讀 229評論 0 0
  • 每個人都有的內褲主要功能是用來遮羞誓沸,但是到了冬天它沒法為我們防風御寒,咋辦壹粟?我們想到的一個辦法就是把內褲改造一下拜隧,...
    chen_000閱讀 1,365評論 0 3
  • Python裝飾器的高級用法(翻譯) 原文地址https://www.codementor.io/python/t...
    城南道閱讀 4,814評論 1 22
  • 匿名函數: lambda 不需要顯式地定義函數洪添,直接傳入匿名函數更方便。 關鍵字lambda表示匿名函數雀费,冒號前面...
    MJXH閱讀 508評論 0 0
  • 晨起6點與冬薇组、珍、穎三友同游聚龍小鎮(zhèn)坐儿。迎面一副亭閣楹聯(lián)映入眼簾:山水凝眸此閣勝景娛天性律胀,廊橋款步是處溫泉...
    田米米閱讀 232評論 0 0