這裏想討論的問題是,若是把python的方法做爲參數傳遞給其餘對象調用,那麼相應的python實例是如何綁定的?html
class C: def callback(self): print('callback') @staticmethod def sc(): print('sc') @classmethod def cc(cls): print('cc') def f(): print('f') def f_with_1_parameter(param): print(param) def do_something(cb): print('do something') cb() if __name__ == '__main__': instance = C() # 它們綁定的是誰? do_something(instance.callback) do_something(C.sc) do_something(C.cc) do_something(f) do_something(instance.f_with_1_parameter) do_something(C.f_with_1_parameter)
python 有這樣的約定,實例方法的第一個參數必然是self
,名字能夠不叫self
,可是第一個參數老是調用方法的類實例。python
類方法的第一個參數必然是cls
,名字能夠不叫cls
,可是第一個參數老是調用該方法的類對象。安全
依照直覺,能夠寫出這樣的代碼。函數
class C: @classmethod def class_method_1(cls): print('class method 1') def instance_method_1(self): print('instance method 1') def do_something(cb): cb() cb(C.class_method_1) cb(C().instance_method_1)
而且它們確實能很好地工做。ui
或許大家注意到了,C()建立了一個匿名的C類實例,而後將這個實例的instance_method_1交給了cb,這看起來是不安全的。this
在C++中,相同的方法須要顯式地將實例指針與實例方法綁定成一個函數對象。C++的實例方法確實是一個「函數」,它的this
沒有python的self
那麼魔幻。指針
直覺上感受不安全,是由於 python 的對象是自動回收的,並且咱們能看得出來:C()彷佛沒有引用了。code
但其實不是,實例方法instance.method
實際上已經綁定了instance
和method
,在實例方法也失去引用以前,instance
不會被釋放。htm
這也是爲何instance_method = instance.method
後,instance_method()
工做得和instance.method()
同樣好的緣由:這是python的魔法,將實例和實例方法綁定在了一塊兒。對象
那麼這種綁定是否是語法糖呢...
我也不知道(誒嘿)。語言規範查閱起來太麻煩了,稍稍不求甚解一下,看看 CPython 這個官方的實現是怎麼處理的吧。
首先是第一問:實例方法若是用class.method
的方式調用,self
參數會綁定成類對象嗎?
class C: def method(self): print(self) C.method()
輸出是
Traceback (most recent call last): File "打碼:/打碼/打碼/打碼/test.py", line 6, in <module> C.method() TypeError: method() missing 1 required positional argument: 'self
看來並不會,class.method
的方式並不會將class
綁定爲self
傳遞給method
,只有經過實例.方法的狀況會綁定實例對象到self
參數。
class C: @classmethod def method(cls): print (cls) instance = C() instance.method()
此次的結果比較神奇,由於它正常執行了。
<class '__main__.C'>
並無報錯。
這應該是相似於原型鏈的機制在其中做祟:instance.class_field
是能夠正常訪問的,不像是C++訪問類變量時須要class::class_field
這樣的特殊語法。
classmethod
裝飾器作的事情感受像是給一個函數包了一層重載了__get__
的類,而後這個包了__get__
的descriptor
打入另外一個owner
類裏,能夠參考下這篇文章Python3 Data Model。
作個具體的實例。
def f(cls): print(cls) def C: pass C.f = classmethod(f) C.f() # 正常執行
這應該是 classmethod
裝飾器實現的方式了(猜想)。
若是在運行時給類插入一個實例方法呢?若是插入的實例方法正常運做,說明這僅僅是一個語法糖:實例.方法會綁定self參數,僅此而已。
def f(self): print(self) class C: pass C.f = f instance = C() instance.f()
輸出是
<__main__.C object at 0x038262B0>
看起來沒錯了,這僅僅是一個語法糖。
用實例.方法
的形式訪問到的實際上是一個這樣子的實例。
class WhoCare: def __init__(self, instance, method): """ 在調用 實例.方法時,實例.方法構成了這樣的一個奇特對象 固然了,別當真。只是爲了說明這種語法背後作了什麼的理解。 """ self._i=instance self._method=method def __call__(self,*args,**kwargs): """ 反正固定了第一個 instance 參數,其餘參數照樣送進去就 ok """ self._method(self._i, *args, **kwargs)