Python-Unbound/Bound method object


本篇主要總結(jié)Python中綁定方法對象(Bound method object)和未綁定方法對象(Unboud method object)的區(qū)別和聯(lián)系。
主要目的是分清楚這兩個極容易混淆的概念凸舵,順便將Python的靜態(tài)方法,類方法及實(shí)例方法加以說明


OK渐苏,下面開始

1. 一個方法引發(fā)的“血案”

類中所定義的函數(shù)稱為方法

舉例:

>>>class Foo(object):
...        def  foo():
...            print 'call foo'   

然后令人困惑的地方就來了:
當(dāng)你嘗試使用類名.方法名調(diào)用函數(shù)foo時菇夸,會出現(xiàn)如下錯誤

>>> Foo.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)

看一下報錯信息發(fā)現(xiàn)需要一個Foo的實(shí)例(instance)來調(diào)用,OK鞠眉,于是調(diào)用如下:

>>> Foo().foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes no arguments (1 given)

-.-!!!
估計(jì)脾氣不好的看到做到這里想要罵街了择诈。
因?yàn)閺淖置嫔峡?code>Foo( ).foo( )并沒有傳遞任何參數(shù),而報錯信息卻顯示(1 given)哗戈。

在Python中一切皆對象荷科,方法是函數(shù)纱注,所以我們來仔細(xì)查看一下函數(shù)對象foo

>>> Foo.foo
<unbound method Foo.foo>
>>> Foo().foo
<bound method Foo.foo of <__main__.Foo object at 0x7ff33b424d50>>

咦~狞贱,發(fā)現(xiàn)一個有趣的現(xiàn)象:
通過類名Foo獲取類函數(shù)屬性foo時煮剧,得到的是unbound method object通過實(shí)例Foo()獲取類的函數(shù)屬性foo時佑颇,得到的是bound method object草娜。
在來看看這兩個對象的類型:

>>> type(Foo.foo)
<type 'instancemethod'>
>>> type(Foo().foo)
<type 'instancemethod'>

于是我們產(chǎn)生了更大的疑問:為什么同樣是實(shí)例方法(instancemethod),獲取方式的不同茬贵,會導(dǎo)致獲得不同的對象呢移袍?

2. bound/unbound method是怎么來的

下面讓我們來一層層揭開這個bound/unbound method的面紗。
首先螟左,我們知道,對于類胶背,其屬性是存放在__dict__字典中喘先,即:

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, '__module__': '__main__', 'foo': <function foo at 0x7ff33b42a5f0>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None})

在其中我們看到了'foo': <function foo at 0x7ff33b42a5f0>窘拯。
然后利用字典查看foo

>>> Foo.__dict__['foo']
<function foo at 0x7ff33b42a5f0>

可以看到foo是一個函數(shù)對象,根據(jù)上一小節(jié)最后一個例子的信息直焙,我們發(fā)現(xiàn),foo是有綁定行為的奔誓。

在Python中使用描述器有翻譯的鏈接)來表示具有“綁定”行為的對象屬性,使用描述器協(xié)議方法來控制對具有綁定行為屬性的訪問和措,這些描述器協(xié)議方法包括:__get__()蜕煌、__set__()__delete__()

根據(jù)上面這段難以讓人理解的描述贫母,我們可以大膽的猜測盒刚,Foo的屬性foo是一個描述器,它通過__get__()方法來控制對foo的訪問橘原。
根據(jù)描述器協(xié)議方法descr.__get__(self, obj, type=None) --> value涡上,我們嘗試如下:

>>> Foo.__dict__['foo'].__get__(None,Foo)
<unbound method Foo.foo>

于是,我們驚訝的看到這個結(jié)果竟然與上一小節(jié)看到的結(jié)果相同芋酌!
這絕不是偶然。
事實(shí)上隔嫡,根據(jù)官方文檔的描述甘穿,調(diào)用Foo.foo時温兼,Python會根據(jù)查找鏈從Foo.__dict__['foo']開始武契,然后查找type(Foo).__dict__['foo'],一路向上查找type(Foo)的所有基類届垫。Foo.foo會被轉(zhuǎn)換為Foo.__dict__['foo'].__get__(None,Foo)全释。
也就是說,我們在代碼中使用Foo.foo實(shí)際上會被轉(zhuǎn)化成
Foo.__dict__['foo'].__get__(None,Foo)
對于根據(jù)描述器協(xié)議方法descr.__get__(self, obj, type=None) --> value的參數(shù)列表妄迁,由于其self參數(shù)在這里被賦予了None,所以沒有給定實(shí)例登淘,因此認(rèn)為是未綁定(unbound)
(當(dāng)然這是一種便于理解的描述,其根本機(jī)制請移步這里)
那么一個很簡單的推理就是:如果self參數(shù)給定了實(shí)例對象黔州,那么,得到的就是bound method牲蜀,如下合冀。

>>> Foo.__dict__['foo'].__get__(Foo(),Foo)
<bound method Foo.foo of <__main__.Foo object at 0x7ff33b424d50>>

因此,可以有如下理解:

  1. 當(dāng)通過類來獲取函數(shù)屬性的時候峭判,得到的是非綁定方法對象
  2. 當(dāng)通過實(shí)例來獲取函數(shù)屬性的時候,得到的是綁定方法對象

3. methods, static method and class method

如果有使用Python方法的經(jīng)驗(yàn)林螃,那么一定注意過self的使用俺泣,請看下面這個例子:

>>> class Foo(object):
...     def foo():
...             print 'call foo'
...     def foo_one(self):
...             print 'call foo_one'
... 
>>> Foo.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)
>>> Foo().foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes no arguments (1 given)
>>> Foo.foo_one()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo_one() must be called with Foo instance as first argument (got nothing instead)
>>> Foo().foo_one()
call foo_one

這個例子定義了兩個method:foo()foo_one(self)伏钠。
可以看到,同樣使用類名.方法名()調(diào)用時熟掂,所報的錯誤相同。但是在使用實(shí)例名.方法名()調(diào)用時素跺,foo_one是可以調(diào)用成功的誉券。
為什么呢?
原因在于當(dāng)使用Foo().foo_one()調(diào)用時踩验,Python做了如下修改:

>>> Foo.foo_one(Foo())
call foo_one

將實(shí)例Foo()作為第一個參數(shù)傳遞進(jìn)去,因此晰甚,函數(shù)foo_one(self)調(diào)用成功。這也解釋了為什么Foo().foo()調(diào)用不成功蓖捶。
因?yàn)?code>foo的定義為foo()扁远,當(dāng)調(diào)用Foo().foo()時,Python做了如下修改:

>>> Foo.foo(Foo())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes no arguments (1 given)

傳入了一個參數(shù)Foo()畅买,所以會出現(xiàn)foo() takes no arguments (1 given)的錯誤。
我曾經(jīng)看到有的人把foo()這種參數(shù)列表中沒有self的方法稱為類方法帝火,而把帶有self的方法稱為實(shí)例方法湃缎,根據(jù)上面的描述可以發(fā)現(xiàn),這種劃分是錯誤的九巡。
那么蹂季,Python中有沒有類方法呢?
答案是偿洁,有。那么如何定義一個類方法呢神凑?

Pyhon 類方法
還是使用上面的例子

>>> class Foo(object):          
...     @classmethod                #定義類方法要點(diǎn)1
...     def foo(cls):               #定義類方法要點(diǎn)2
...             print 'call foo'
... 
>>> Foo.foo()
call foo
>>> Foo().foo()
call foo

定義類方法需要注意兩點(diǎn):1. 添加@classmethod;2. 添加cls參數(shù)
這樣定義的類方法可以通過類名.方法名()的形式調(diào)用鹃唯,也可以通過實(shí)例.方法名()的形式調(diào)用。

看到這里會發(fā)現(xiàn)坡慌,在Python中定義方法,總要帶兩個參數(shù)self或者cls跪者。其中通過self限定的method必須使用實(shí)例才能調(diào)用。
那么很自然的一個疑問是逗概,能不能定義不包含selfcls的方法呢忘衍?像最開始的例子中foo()那樣。答案是有的枚钓,辦法就是加@staticmethod修飾器。
這種被@staticmethod修飾器修飾的方法星掰,稱為靜態(tài)方法

Python 靜態(tài)方法
除了類方法嫩舟,還有靜態(tài)方法,請看下面這個例子:

>>> class Foo(object):
...     @staticmethod
...     def foo():
...             print 'call foo'
... 
>>> Foo.foo()
call foo
>>> Foo().foo()
call foo

靜態(tài)方法可以通過類名.方法名()實(shí)例.方法名()的形式調(diào)用威始。
查看type結(jié)果如下:

>>> type(Foo.foo)
<type 'function'>

可以看到像街,靜態(tài)方法的類型是function,而類方法的類型是instancemethod脓斩。

總結(jié)

最后來總結(jié)一下:
從Python方法定義的角度出發(fā),可以分為三種:
1.第一個參數(shù)是self;
2.第一個參數(shù)是cls;
3.參數(shù)既不含self也不含cls
對于第一種方法随静,必須通過實(shí)例.方法名()類名.方法名(實(shí)例)的形式調(diào)用吗讶;
對于第二種,可以通過實(shí)例.方法名()類名.方法名()的形式調(diào)用重绷,不能通過類名.方法名(實(shí)例)的形式調(diào)用;
對于第三種昭卓,方法即是普通函數(shù),但是必須通過實(shí)例.方法名()類名.方法名()的形式調(diào)用候醒,不能通過其他形式調(diào)用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倒淫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子昌简,更是在濱河造成了極大的恐慌,老刑警劉巖谦疾,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件念恍,死亡現(xiàn)場離奇詭異晚顷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)该默,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門栓袖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人音榜,你說我怎么就攤上這事≡穑” “怎么了违霞?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵买鸽,是天一觀的道長。 經(jīng)常有香客問我底燎,道長弹砚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮茅诱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翎卓。我一直安慰自己摆寄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布逗扒。 她就那樣靜靜地躺著欠橘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪黍檩。 梳的紋絲不亂的頭發(fā)上痹升,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音肛跌,去河邊找鬼察郁。 笑死,一個胖子當(dāng)著我的面吹牛皮钠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乔夯,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼侧纯!你這毒婦竟也來了甲脏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娜氏,失蹤者是張志新(化名)和其女友劉穎贸弥,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茂腥,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡最岗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年朝捆,在試婚紗的時候發(fā)現(xiàn)自己被綠了般渡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡芙盘,死狀恐怖驯用,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情儒老,我是刑警寧澤蝴乔,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站驮樊,受9級特大地震影響薇正,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜囚衔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一挖腰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧练湿,春花似錦肥哎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碧信。三九已至砰碴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間趁尼,已是汗流浹背酥泞。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工芝囤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悯姊,地道東北人悯许。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像睛藻,于是被迫代替她去往敵國和親店印。 傳聞我的和親對象是個殘疾皇子按摘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理溅固,服務(wù)發(fā)現(xiàn)侍郭,斷路器,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,719評論 0 9
  • 從明天開始卖局,是新的一天吼驶,又回到了教育行業(yè)店煞。 最初上學(xué)時,最初得知是學(xué)的師范專業(yè)酒请,一百個不愿意不甘心羞反,自顧自的把...
    小霏鈴子閱讀 449評論 0 1
  • 支付寶業(yè)務(wù)協(xié)作協(xié)議 您(以下簡稱“甲方”)與支付寶(中國)網(wǎng)絡(luò)技術(shù)有限公司(以下稱“支付寶”或“乙方”)經(jīng)友好協(xié)商...
    山東老李投融建閱讀 2,883評論 0 1