Python裝飾器類的使用——bound/unbound method

引言

在學(xué)習(xí)裝飾器類方法時發(fā)現(xiàn)忙菠,如果裝飾器類只有__ call __方法則只能裝飾普通的函數(shù)(function),不能對實例方法(instance method)進行裝飾坏瞄,經(jīng)過研究發(fā)現(xiàn)與python的函數(shù)實現(xiàn)有關(guān)仁堪。本文就此展開討論媳纬,并給出一個完整的裝飾器類實現(xiàn)方法球匕。

問題發(fā)現(xiàn)

我們都知道纹磺,當(dāng)我們調(diào)用一個類的實例方法時,實例會作為方法的第一個入?yún)elf亮曹。而在下面的例子中橄杨,實例方法foo被裝飾器類Decorator裝飾后秘症,Decorated().foo('hello, world')的第一個入?yún)elf并不是Decorated實例,而是我們傳進去的"hello, world"式矫,很明顯這是不符合我們預(yù)期的乡摹。實際上這與python的函數(shù)調(diào)用有關(guān),bound method(通過實例調(diào)用的方法)會自動將實例本身作為方法的第一個入?yún)⒉勺鴉unction和unbound method(類直接調(diào)用的方法)不會自動注入實例趟卸。當(dāng)我們用裝飾器裝飾后,被傳入Decorator中的func失去了bound屬性氏义,自然也不會自動注入實例。

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

    def __call__(self, *args, **kwargs):
        print("Decorator: self: {}, args: {}, kwargs: {}".format(self, args, kwargs))
        return self.func(*args, **kwargs)

class Decorated(object):
    @Decorator
    def foo(self, *args, **kwargs):
        print("Method Decorated: self: {}, args: {}, kwargs: {}".format(self, args, kwargs))

Decorated().foo('hello, world')
# Decorator: self: <__main__.Decorator object at 0x7f978aeb0290>, args: ('hello, world',), kwargs: {}
# Method Decorated: self: hello, world, args: (), kwargs: {}

function图云、bound method與unbound method的區(qū)別

上一節(jié)我們說到了bound method和unboud method惯悠,本小節(jié)將對這些概念進行具體說明。

 class Cls(object):

     def foo(self):
        pass
 instance = Cls()

 print 'foo' in Cls.__dict__                # True
 print 'foo' in instance.__dict__           # False

 print Cls.foo              # <unbound method Cls.foo>
 print instance.foo         # <bound method Cls.foo of <__main__.Cls object at 0x7ff05ddef390>>
 print Cls.__dict__['foo']  # <function foo at 0x7ff05dde98c0>

我們發(fā)現(xiàn)竣况,foo是以function類型存在類的__dict__中的克婶,但通過實例訪問是會得到一個bound method,通過類訪問是會得到一個unbound method丹泉。這是因為在python中情萤,所有的函數(shù)都是非資料描述器(no data descriptor)。通過類或?qū)嵗L問時摹恨,會被__get__方法會包裝一層返回unbound method或bound method筋岛,而unboud和bound的區(qū)別在于method的im_self是否為空。bound method會自動將instance作為第一個入?yún)⑸购澹鴘nbound method對參數(shù)不做任何處理睁宰。
在上一小節(jié)的例子中,foo作為Decorator的一個成員變量寝凌,其type是一個function柒傻,所以不會自動地注入實例。

Cls.foo會被翻譯成Cls.__dict __[‘foo’].__get__(None, Cls)较木,返回一個unbound method(im_self is None)
instance.foo會被翻譯成type(instance).__dic __[‘foo’].__get__(instance, type(instance))红符,返回一個bound method(im_self == instance)
instance.foo(args, kwargs) 等價于 Cls.foo(instance, args, kwargs)

修改方法

通過分析,我們發(fā)現(xiàn)根本原因在于伐债,被裝飾的foo作為Decorator實例的成員變量预侯,其類型是一個function,失去了bound屬性泳赋,不能自動地將instance實例注入到函數(shù)的第一個參數(shù)中去雌桑。為了解決這個,我們可以將Decorator實現(xiàn)成一個描述器祖今,當(dāng)被類或?qū)嵗L問時校坑,通過types.MethodType將Decorator實例包裝成一個method拣技。

class Decorator(object):

    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        return types.MethodType(self, instance, owner)

    def __call__(self, *args, **kwargs):
        print("Decorator: self: {}, args: {}, kwargs: {}".format(self, args, kwargs))
        return self.func(*args, **kwargs)

參考文獻

The Inside Story on New-Style Classes
python - Instancemethod or function? - Stack Overflow
Chris’s Wiki :: blog/python/HowFunctionsToMethods
Descriptor HowTo Guide — Python 2.7.15 documentation
python - How can I decorate an instance method with a decorator class? - Stack Overflow
Glossary — Python 2.7.15 documentation

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市耍目,隨后出現(xiàn)的幾起案子膏斤,更是在濱河造成了極大的恐慌,老刑警劉巖邪驮,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莫辨,死亡現(xiàn)場離奇詭異,居然都是意外死亡毅访,警方通過查閱死者的電腦和手機沮榜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喻粹,“玉大人蟆融,你說我怎么就攤上這事∈匚兀” “怎么了型酥?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長查乒。 經(jīng)常有香客問我弥喉,道長,這世上最難降的妖魔是什么玛迄? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任由境,我火速辦了婚禮,結(jié)果婚禮上憔晒,老公的妹妹穿的比我還像新娘藻肄。我一直安慰自己,他們只是感情好拒担,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布嘹屯。 她就那樣靜靜地躺著,像睡著了一般从撼。 火紅的嫁衣襯著肌膚如雪州弟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天低零,我揣著相機與錄音婆翔,去河邊找鬼。 笑死掏婶,一個胖子當(dāng)著我的面吹牛啃奴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雄妥,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼最蕾,長吁一口氣:“原來是場噩夢啊……” “哼依溯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瘟则,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤黎炉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后醋拧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慷嗜,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年丹壕,在試婚紗的時候發(fā)現(xiàn)自己被綠了庆械。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡菌赖,死狀恐怖干奢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盏袄,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布薄啥,位于F島的核電站辕羽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏垄惧。R本人自食惡果不足惜刁愿,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望到逊。 院中可真熱鬧铣口,春花似錦、人聲如沸觉壶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铜靶。三九已至叔遂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間争剿,已是汗流浹背已艰。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚕苇,地道東北人哩掺。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像涩笤,于是被迫代替她去往敵國和親嚼吞。 傳聞我的和親對象是個殘疾皇子盒件,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)誊薄,斷路器履恩,智...
    卡卡羅2017閱讀 134,638評論 18 139
  • 包(lib)、模塊(module) 在Python中呢蔫,存在包和模塊兩個常見概念切心。 模塊:編寫Python代碼的py...
    清清子衿木子水心閱讀 3,801評論 0 27
  • 文/博土 終于記清楚日子了,因為開始營業(yè)了片吊,每天都要記賬绽昏。 二姐說他們在他們茶館旁邊找一個一室的自己住,我得自己找...
    博土閱讀 129評論 0 0
  • 最近學(xué)習(xí)英語估計把耳朵聽壞了俏脊,白天一整天感覺有好多只淘氣的小蜜蜂在耳朵里“嗡嗡”作響全谤。要是繼續(xù)這樣下去:革命尚未成...
    修墨68閱讀 215評論 0 1
  • 個人目標(biāo) 8月完成閱讀2本書 睡前背成語10個 工作尋求新突破,改變爷贫。 每周持續(xù)微課學(xué)習(xí)认然!及時...
    檸檬少女心閱讀 205評論 0 0