摘要:本篇主要總結(jié)中綁定方法對象和未綁定方法對象的區(qū)別和聯(lián)系。在中使用描述器有翻譯的鏈接來表示具有綁定行為的對象屬性,使用描述器協(xié)議方法來控制對具有綁定行為屬性的訪問,這些描述器協(xié)議方法包括和。其中通過限定的必須使用實(shí)例才能調(diào)用。
本篇主要總結(jié)Python中綁定方法對象(Bound method object)和未綁定方法對象(Unboud method object)的區(qū)別和聯(lián)系。
主要目的是分清楚這兩個(gè)極容易混淆的概念,順便將Python的靜態(tài)方法,類方法及實(shí)例方法加以說明
OK,下面開始
1. 一個(gè)方法引發(fā)的“血案”類中所定義的函數(shù)稱為方法
舉例:
>>>class Foo(object): ... def foo(): ... print "call foo"
然后令人困惑的地方就來了:
當(dāng)你嘗試使用類名.方法名調(diào)用函數(shù)foo時(shí),會(huì)出現(xiàn)如下錯(cuò)誤
>>> Foo.foo() Traceback (most recent call last): File "", line 1, in TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)
看一下報(bào)錯(cuò)信息發(fā)現(xiàn)需要一個(gè)Foo的實(shí)例(instance)來調(diào)用,OK,于是調(diào)用如下:
>>> Foo().foo() Traceback (most recent call last): File "", line 1, in TypeError: foo() takes no arguments (1 given)
-.-!!!
估計(jì)脾氣不好的看到做到這里想要罵街了。
因?yàn)閺淖置嫔峡碏oo( ).foo( )并沒有傳遞任何參數(shù),而報(bào)錯(cuò)信息卻顯示(1 given)。
在Python中一切皆對象,方法是函數(shù),所以我們來仔細(xì)查看一下函數(shù)對象foo
>>> Foo.foo>>> Foo().foo >
咦~,發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象:
通過類名Foo獲取類函數(shù)屬性foo時(shí),得到的是unbound method object,通過實(shí)例Foo()獲取類的函數(shù)屬性foo時(shí),得到的是bound method object。
在來看看這兩個(gè)對象的類型:
>>> type(Foo.foo)>>> type(Foo().foo)
于是我們產(chǎn)生了更大的疑問:為什么同樣是實(shí)例方法(instancemethod),獲取方式的不同,會(huì)導(dǎo)致獲得不同的對象呢?
2. bound/unbound method是怎么來的下面讓我們來一層層揭開這個(gè)bound/unbound method的面紗。
首先,我們知道,對于類,其屬性是存放在__dict__字典中,即:
>>> Foo.__dict__ dict_proxy({"__dict__":, "__module__": "__main__", "foo": , "__weakref__": , "__doc__": None})
在其中我們看到了"foo":
然后利用字典查看foo:
>>> Foo.__dict__["foo"]
可以看到foo是一個(gè)函數(shù)對象,根據(jù)上一小節(jié)最后一個(gè)例子的信息,我們發(fā)現(xiàn),foo是有綁定行為的。
在Python中使用描述器(有翻譯的鏈接)來表示具有“綁定”行為的對象屬性,使用描述器協(xié)議方法來控制對具有綁定行為屬性的訪問,這些描述器協(xié)議方法包括:__get__()、__set__()和__delete__()。
根據(jù)上面這段難以讓人理解的描述,我們可以大膽的猜測,F(xiàn)oo的屬性foo是一個(gè)描述器,它通過__get__()方法來控制對foo的訪問。
根據(jù)描述器協(xié)議方法descr.__get__(self, obj, type=None) --> value,我們嘗試如下:
>>> Foo.__dict__["foo"].__get__(None,Foo)
于是,我們驚訝的看到這個(gè)結(jié)果竟然與上一小節(jié)看到的結(jié)果相同!
這絕不是偶然。
事實(shí)上,根據(jù)官方文檔的描述,調(diào)用Foo.foo時(shí),Python會(huì)根據(jù)查找鏈從Foo.__dict__["foo"]開始,然后查找type(Foo).__dict__["foo"],一路向上查找type(Foo)的所有基類。Foo.foo會(huì)被轉(zhuǎn)換為Foo.__dict__["foo"].__get__(None,Foo)。
也就是說,我們在代碼中使用Foo.foo實(shí)際上會(huì)被轉(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ī)制請移步這里)
那么一個(gè)很簡單的推理就是:如果self參數(shù)給定了實(shí)例對象,那么,得到的就是bound method,如下。
>>> Foo.__dict__["foo"].__get__(Foo(),Foo)>
因此,可以有如下理解:
當(dāng)通過類來獲取函數(shù)屬性的時(shí)候,得到的是非綁定方法對象
當(dāng)通過實(shí)例來獲取函數(shù)屬性的時(shí)候,得到的是綁定方法對象
如果有使用Python方法的經(jīng)驗(yàn),那么一定注意過self的使用,請看下面這個(gè)例子:
>>> class Foo(object): ... def foo(): ... print "call foo" ... def foo_one(self): ... print "call foo_one" ... >>> Foo.foo() Traceback (most recent call last): File "", line 1, in TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead) >>> Foo().foo() Traceback (most recent call last): File " ", line 1, in TypeError: foo() takes no arguments (1 given) >>> Foo.foo_one() Traceback (most recent call last): File " ", line 1, in TypeError: unbound method foo_one() must be called with Foo instance as first argument (got nothing instead) >>> Foo().foo_one() call foo_one
這個(gè)例子定義了兩個(gè)method:foo()和foo_one(self)。
可以看到,同樣使用類名.方法名()調(diào)用時(shí),所報(bào)的錯(cuò)誤相同。但是在使用實(shí)例名.方法名()調(diào)用時(shí),foo_one是可以調(diào)用成功的。
為什么呢?
原因在于當(dāng)使用Foo().foo_one()調(diào)用時(shí),Python做了如下修改:
>>> Foo.foo_one(Foo()) call foo_one
將實(shí)例Foo()作為第一個(gè)參數(shù)傳遞進(jìn)去,因此,函數(shù)foo_one(self)調(diào)用成功。這也解釋了為什么Foo().foo()調(diào)用不成功。
因?yàn)閒oo的定義為foo(),當(dāng)調(diào)用Foo().foo()時(shí),Python做了如下修改:
>>> Foo.foo(Foo()) Traceback (most recent call last): File "", line 1, in TypeError: foo() takes no arguments (1 given)
傳入了一個(gè)參數(shù)Foo(),所以會(huì)出現(xiàn)foo() takes no arguments (1 given)的錯(cuò)誤。
我曾經(jīng)看到有的人把foo()這種參數(shù)列表中沒有self的方法稱為類方法,而把帶有self的方法稱為實(shí)例方法,根據(jù)上面的描述可以發(fā)現(xiàn),這種劃分是錯(cuò)誤的。
那么,Python中有沒有類方法呢?
答案是,有。那么如何定義一個(gè)類方法呢?
還是使用上面的例子
>>> 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)用。
看到這里會(huì)發(fā)現(xiàn),在Python中定義方法,總要帶兩個(gè)參數(shù)self或者cls。其中通過self限定的method必須使用實(shí)例才能調(diào)用。
那么很自然的一個(gè)疑問是,能不能定義不包含self及cls的方法呢?像最開始的例子中foo()那樣。答案是有的,辦法就是加@staticmethod修飾器。
這種被@staticmethod修飾器修飾的方法,稱為靜態(tài)方法
除了類方法,還有靜態(tài)方法,請看下面這個(gè)例子:
>>> 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)
可以看到,靜態(tài)方法的類型是function,而類方法的類型是instancemethod。
總結(jié)最后來總結(jié)一下:
從Python方法定義的角度出發(fā),可以分為三種:
1.第一個(gè)參數(shù)是self;
2.第一個(gè)參數(shù)是cls;
3.參數(shù)既不含self也不含cls的
對于第一種方法,必須通過實(shí)例.方法名()或類名.方法名(實(shí)例)的形式調(diào)用;
對于第二種,可以通過實(shí)例.方法名()或類名.方法名()的形式調(diào)用,不能通過類名.方法名(實(shí)例)的形式調(diào)用;
對于第三種,方法即是普通函數(shù),但是必須通過實(shí)例.方法名()或類名.方法名()的形式調(diào)用,不能通過其他形式調(diào)用
轉(zhuǎn)載自:簡書-_Zhao_ -Python-Unbound/Bound method object
這篇博文講解的非常清楚,值得一看
SF-python函數(shù)和類的一些研究
Python 中的方法綁定
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/45405.html
摘要:接下來要看看這個(gè)訂閱者的具體實(shí)現(xiàn)了實(shí)現(xiàn)訂閱者作為和之間通信的橋梁,主要做的事情是在自身實(shí)例化時(shí)往屬性訂閱器里面添加自己自身必須有一個(gè)方法待屬性變動(dòng)通知時(shí),能調(diào)用自身的方法,并觸發(fā)中綁定的回調(diào),則功成身退。 本文能幫你做什么?1、了解vue的雙向數(shù)據(jù)綁定原理以及核心代碼模塊2、緩解好奇心的同時(shí)了解如何實(shí)現(xiàn)雙向綁定為了便于說明原理與實(shí)現(xiàn),本文相關(guān)代碼主要摘自vue源碼, 并進(jìn)行了簡化改造,...
摘要:如今查找結(jié)果有誤,說明繼承鏈?zhǔn)清e(cuò)誤的,因而極有可能是出錯(cuò)。真相一切都源于裝飾器語法糖。核心思路就是不要更改被裝飾名稱的引用。 本文首發(fā)于我的博客,轉(zhuǎn)載請注明出處 《神坑》系列將會(huì)不定期更新一些可遇而不可求的坑防止他人入坑,也防止自己再次入坑 簡化版問題 現(xiàn)有兩個(gè) View 類: class View(object): def method(self): #...
摘要:項(xiàng)目地址迭代器與生成器迭代器與生成器是中比較常用又很容易混淆的兩個(gè)概念,今天就把它們梳理一遍,并舉一些常用的例子。生成器前面說到創(chuàng)建迭代器有種方法,其中第三種就是生成器。 項(xiàng)目地址:https://git.io/pytips 迭代器與生成器 迭代器(iterator)與生成器(generator)是 Python 中比較常用又很容易混淆的兩個(gè)概念,今天就把它們梳理一遍,并舉一些常用的例...
閱讀 2797·2021-09-23 11:44
閱讀 1684·2021-09-13 10:24
閱讀 2634·2021-09-08 09:36
閱讀 1241·2019-08-30 15:54
閱讀 2262·2019-08-30 13:54
閱讀 3323·2019-08-30 10:57
閱讀 1859·2019-08-29 18:43
閱讀 3627·2019-08-29 15:10