python 多重类继承__init__
目的
项目中遇到多重类继承的问题,想调用父类构造函数中的内容,调试了一两个小时,遇到两个问题。
- 说不存在某个父类的函数;
- 报MRO列表错误;
查询了相关的文档,大致是讲解父类的继承,没有涉及到多重继承,以及多重继承构造函数的问题,这里总结一下。
调用父类方法
想在子类中调用父类的某个已经被覆盖的方法:
解决方案
为了调用父类(超类)的一个方法,可以使用 super()
函数,比如
class A: def spam(self): print('A.spam') class B(A): def spam(self): print('B.spam') super().spam() # Call parent spam()
super()
函数的一个常见用法是在 __init__()
方法中确保父类被正确的初始化了:
class A: def __init__(self): self.x = 0 class B(A): def __init__(self): super().__init__() self.y = 1
super()
的另外一个常见用法出现在覆盖Python特殊方法的代码中,比如:
class Proxy: def __init__(self, obj): self._obj = obj # Delegate attribute lookup to internal obj def __getattr__(self, name): return getattr(self._obj, name) # Delegate attribute assignment def __setattr__(self, name, value): if name.startswith('_'): super().__setattr__(name, value) # Call original __setattr__ else: setattr(self._obj, name, value)
在上面代码中,__setattr__()
的实现包含一个名字检查。 如果某个属性名以下划线(_)开头,就通过 super() 调用原始的 __setattr__()
, 否则的话就委派给内部的代理对象 self._obj
去处理。 这看上去有点意思,因为就算没有显式的指明某个类的父类, super()
仍然可以有效的工作。
深入 super()
看了上面的使用,你可能会觉得 super 的使用很简单,无非就是获取了父类,并调用父类的方法。其实,在上面的情况下,super 获得的类刚好是父类,但在其他情况就不一定了,super 其实和父类没有实质性的关联。
让我们看一个稍微复杂的例子,涉及到多重继承,代码如下:
class Base(object): def __init__(self): print "enter Base" print "leave Base" class A(Base): def __init__(self): print "enter A" super(A, self).__init__() print "leave A" class B(Base): def __init__(self): print "enter B" super(B, self).__init__() print "leave B" class C(A, B): def __init__(self): print "enter C" super(C, self).__init__() print "leave C"
其中,Base 是父类,A, B 继承自 Base, C 继承自 A, B,它们的继承关系是一个典型的『菱形继承』,如下:
Base / \ / \ A B \ / \ / C
现在,让我们看一下使用:
c = C() # output enter C enter A enter B enter Base leave Base leave B leave A leave C
如果你认为 super 代表『调用父类的方法』,那你很可能会疑惑为什么 enter A 的下一句不是 enter Base 而是 enter B。原因是,super 和父类没有实质性的关联,现在让我们搞清 super 是怎么运作的。
MRO 列表
事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表:
>>> C.mro() # or C.__mro__ or C().__class__.mro() [__main__.C, __main__.A, __main__.B, __main__.Base, object]
那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法来实现的,这里我们就不去深究这个算法了,感兴趣的读者可以自己去了解一下,总的来说,一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则:
- 子类永远在父类前面
- 如果有多个父类,会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
super原理
super
的工作原理如下:
def super(cls, inst): mro = inst.__class__.mro() return mro[mro.index(cls) + 1]
其中,cls 代表类,inst 代表实例,上面的代码做了两件事:
- 获取 inst 的 MRO 列表
- 查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]
当你使用 super(cls, inst)
时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。
现在,让我们回到前面的例子。
首先看类 C 的 __init__
方法:
super(C, self).__init__()
这里的 self 是当前 C 的实例,self.class.mro()
结果是:
[__main__.C, __main__.A, __main__.B, __main__.Base, object]
可以看到,C 的下一个类是 A,于是,跳到了 A 的 __init__
,这时会打印出 enter A,并执行下面一行代码:
super(A, self).__init__()
注意,这里的 self 也是当前 C 的实例,MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个类,发现是 B,于是,跳到了 B 的 __init__
,这时会打印出 enter B,而不是 enter Base。
整个过程还是比较清晰的,关键是要理解 super 的工作方式,而不是想当然地认为 super 调用了父类的方法。
总结
- 事实上,super 和父类没有实质性的关联;
- super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类;
- 在使用多重继承时候,注意继承顺序;
- 如果使用到父类的构造函数,要使用
super()
参考
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
datax:如何使用datax
一、如何编译 从https://github.com/alibaba/DataX将工程代码下载到本地,配置好java和maven环境,执行命令mvn -U clean package assembly:assembly -Dmaven.test.skip=true即可实现打包,执行完毕之后会在工程目录下面有一个target目录,包含有一个datax的文件夹和datax.tar.gz文件即是我们需要的文件二、如何使用 进入datax/bin目录下,新建test.json文件,文件内容为: { "job": { "setting": { "speed": { "channel": 1 } }, "content": [ { "reader": { "name": "txtfilereader", "parameter": { "path": ["/XXX/src"], "encoding": "UTF-8", "column": [ { "index": 0, "type": "long" }, { "index": 1, "type": "long" } ], "fieldDelimit...
- 下一篇
datax源码阅读一:python文件
一、前面主要是怎么使用datax和datax的插件编写,后面主要说明源码阅读部分,python相关文件 二、datax关键代码(python datax.py test.json) 1、datax.py文件 printCopyright() parser = getOptionParser(sys.argv[1:]) options, args = parser.parse_args(sys.argv[1:]) if options.reader is not None and options.writer is not None: generateJobConfigTemplate(options.reader,options.writer) sys.exit(RET_STATE['OK']) if len(args) == 0: parser.print_help() sys.exit(RET_STATE['FAIL']) startCommand = buildStartCommand(options, args) child_process = subprocess.Popen...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS关闭SELinux安全模块
- Hadoop3单机部署,实现最简伪集群
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2更换Tomcat为Jetty,小型站点的福音