python中的回调和关于首参数的绑定的说法 !!
函数参数的绑定和调用方式
这里想讨论的问题是,如果把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)```
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java 工程师成神之路 | 2019正式版
基础篇 01面向对象 → 什么是面向对象面向对象、面向过程 面向对象的三大基本特征和五大基本原则 → 平台无关性Java 如何实现的平台无关 JVM 还支持哪些语言(Kotlin、Groovy、JRuby、Jython、Scala) → 值传递值传递、引用传递 为什么说 Java 中只有值传递 → 封装、继承、多态什么是多态、方法重写与重载 Java 的继承与实现 构造函数与默认构造函数 类变量、成员变量和局部变量 成员变量和方法作用域 02 Java 基础知识 → 基本数据类型7 种基本数据类型:整型、浮点型、布尔型、字符型 整型中 byte、short、int、long 的取值范围 什么是浮点型?什么是单精度和双精度?为什么不能用浮点型表示金额? → 自动拆装箱什么是包装类型、什么是基本类型、什么是自动拆装箱 Integer 的缓存机制 → String字符串的不可变性 JDK 6 和 JDK 7 中 substring 的原理及区别、 replaceFirst、replaceAll、replace 区别、 String 对“+”的重载、字符串拼接的几种方式和区别 String.v...
- 下一篇
Unity3D使用经验总结 优点篇
一、可定制的IDE环境U3D这种ALL IN ONE的设计思路,我在一个叫神咒的代码中见到过。 集所有编辑器于一身。 虽然神咒的编辑器不能自由扩展,但由于是公司内部的引擎,所以,它的使用,也很方便。 比如,在场景中突然想要对一个模型的材质进行编辑,则选中此模型,右键,弹出材质编辑器即可。 U3D的组件式思路,将这种关系变得更加紧密。 你都感觉不到自己在使用一个材质编辑器。 你会觉得,你是在操作这个模型本身。 它的材质,它的碰撞器,它的对象结构等等。回想一开始进入游戏行业的时候,天天啃着代码。 当时觉得代码就是一切,各种认为很牛X的代码,都忍不住读上一番。 而随着时间的推移,特别是经过项目的洗礼后。 突然发现编辑器是多么的重要。 就我做的第一个页游来说,起手前两个星期,我们就做了动画编辑器,场景编辑器。而最终证明,因为这两个简陋的编辑器,使我们后面的工作变得更加容易。因此,一个好的引擎,必定得先有一个功能完备的编辑器。 二、基于Mono的开发脚本C/C++无疑是图形界的宠儿,也没有人想过用另一种语言来替代它。即使是U3D,亦是如此。 但是,早期使用C/C++编写的引擎,都理所当然地使用C...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Red5直播服务器,属于Java语言的直播服务器
- CentOS6,CentOS7官方镜像安装Oracle11G
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池