您现在的位置是:首页 > 文章详情

python中的回调和关于首参数的绑定的说法 !!

日期:2019-02-17点击:328

函数参数的绑定和调用方式

这里想讨论的问题是,如果把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的魔法,将实例和实例方法绑定在了一起。 ![](https://yqfile.alicdn.com/fc1bfb301cd26b5df1dfdf2024f6e4e900856cb7.png) **语法糖?** 那么这种绑定是不是语法糖呢... 我也不知道(诶嘿)。语言规范查阅起来太麻烦了,稍稍不求甚解一下,看看 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参数。 **实例.类方法**

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)```
原文链接:https://yq.aliyun.com/articles/690606
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章