这里想讨论的问题是,若是把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)