函數參數的綁定和調用方式
這裡想讨論的問題是,如果把python的方法作為參數傳遞給其他對象調用,那麼相應的python執行個體是如何綁定的?
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,但是第一個參數總是調用方法的類執行個體。
類方法的第一個參數必然是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)```
并且它們确實能很好地工作。
或許你們注意到了,C()建立了一個匿名的C類執行個體,然後将這個執行個體的instance_method_1交給了cb,這看起來是不安全的。
在C++中,相同的方法需要顯式地将執行個體指針與執行個體方法綁定成一個函數對象。C++的執行個體方法确實是一個“函數”,它的this沒有python的self那麼魔幻。
直覺上感覺不安全,是因為 python 的對象是自動回收的,而且我們能看得出來:C()似乎沒有引用了。
但其實不是,執行個體方法instance.method實際上已經綁定了instance和method,在執行個體方法也失去引用之前,instance不會被釋放。
這也是為什麼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
C.method()
TypeError: method() missing 1 required positional argument: 'self
看來并不會,class.method的方式并不會将class綁定為self傳遞給method,隻有通過執行個體.方法的情況會綁定執行個體對象到self參數。
**執行個體.類方法**
@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)```