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