super函数没有那么简单-super原理剖析
开始之前,先出一道题:
#super函数探讨
class A(object):
def __init__(self):
print 'A.__init__' class B(A):
def __init__(self):
super(B, self).__init__()
print 'B.__init__' class C(A):
def __init__(self):
super(C, self).__init__()
print 'C.__init__' class D(B, C):
def __init__(self):
super(D, self).__init__()
print 'D.__init__' d = D()
上面的运行结果是什么?
是下面的结果吗?
A.__init__
B.__init__
D.__init__
正确答案:
A.__init__
C.__init__
B.__init__
D.__init__
有没有疑惑?super()函数不是调用指定类的父类的方法吗!打印了A.__init__下一句为什么是C.__init__呢?
根本原因是:
super
和父类没有实质性的关联
首先,我们知道新式类采用广度优先算法,我们来看一下上面的继承关系:
那么,Python是如何实现继承的,继承顺序又是由谁决定的呢? 对于你定义的每一个类而已,Python会计算出一个所谓的方法解析顺序(MRO Method Resolution Order)列表。类的继承顺序就是由这个MRO决定的。
MRO通过class.__mro__来查看,我们来打印一下上面例子中的MRO:
print D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
注意__mro__是类的属性,实例没有该属性
这个MRO列表就是一个简单的所有基类的线性顺序表。为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。 它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1)子类会先于父类被检查
2)多个父类会根据它们在列表中的顺序被检查
3)如果对下一个类存在两个合法的选择,选择第一个父类
好像还是没明白为什么例子中,打印了A.__init__下一句为什么是C.__init__呢?
我们使用一个函数来解释一下super的原理:
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
其中,cls 代表类,inst 代表实例,上面的代码做了两件事:
1)获取 inst 的 MRO 列表
2)查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]
当你使用 super(cls, inst)
时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。
是不是有一种豁然开朗的赶脚!让我们回到例子中,这里我画出了整个流程;
从上面的流程图就可以看出打印的顺序是对的!
了解了super的原理,那么也就可以理解下面这段有趣的代码了:
1)执行下面代码
class A(object):
def go(self):
print 'A go'
super(A, self).go() a = A()
a.go()
会报错:
AttributeError: 'super' object has no attribute 'go'
2)执行下面代码:
class A(object):
def go(self):
print 'A go'
super(A, self).go() class B(object):
def go(self):
print 'B go' class C(A, B):
pass c = C()
c.go()
不会报错,结果为:
A go
B go
充分说明了super
和父类没有实质性的关联
另外,我们想出了super以外,还有一种直接调用父类方法的方法,如下:
#super函数探讨
class A(object):
def __init__(self):
print 'A.__init__' class B(A):
def __init__(self):
# super(B, self).__init__()
A.__init__(self)
print 'B.__init__' class C(A):
def __init__(self):
# super(C, self).__init__()
A.__init__(self)
print 'C.__init__' class D(B, C):
def __init__(self):
# super(D, self).__init__()
B.__init__(self)
C.__init__(self)
print 'D.__init__' d = D()
为什么不用这种方法呢?我们运行一下,看一下,结果为:
A.__init__
B.__init__
A.__init__
C.__init__
D.__init__
很明显,A的构造函数运行了两次,这不是我们所希望的;所以还是用super吧!
super函数没有那么简单-super原理剖析的更多相关文章
- Python中的super函数,你熟吗?
摘要:经常有朋友问,学 Python 面向对象时,翻阅别人代码,会发现一个 super() 函数,那这个函数的作用到底是什么? 本文分享自华为云社区<Python中的super函数怎么学,怎么解 ...
- python基础----多态与多态性、super函数用法、继承原理
一.多态与多态性 ㈠多态: 多态指的是一类事物有多种形态, ...
- 由Python的super()函数想到的
python-super *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !im ...
- Python super() 函数的概念和例子
概念: super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO).重 ...
- Python super() 函数
super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果重定义某个方法,该方法会覆盖父类的同名方法,但有时 ...
- python super()函数详解
引言: 在类的多继承使用场景中,重写父类的方法时,可能会考虑到需要重新调用父类的方法,所以super()函数就是比较使用也很必要的解决方法: 文章来源: http://www.cnblogs.com/ ...
- Python面试题之Super函数
这是个高大上的函数,在python装13手册里面介绍过多使用可显得自己是高手 23333. 但其实他还是很重要的. 简单说, super函数是调用下一个父类(超类)并返回该父类实例的方法. 这里的下一 ...
- python 中 super函数的使用
转载地址:http://python.jobbole.com/86787/ 1.简单的使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我 ...
- Python3中的super()函数详解
关于Python3中的super()函数 我们都知道,在Python3中子类在继承父类的时候,当子类中的方法与父类中的方法重名时,子类中的方法会覆盖父类中的方法, 那么,如果我们想实现同时调用父类和子 ...
随机推荐
- .Net中关于相等的问题
在.Net框架中,如果您查看所有类型的的基类:System.Object类,将找到如下4个与相等判断的方法: static Equals() virtual Equals() static Refer ...
- CloseHandle 函数--关闭一个句柄
CloseHandle函数 来源:https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx 作用 ...
- 【ztree】ztree例子
<script language="javascript" type="text/javascript" src="js/jquery.js&q ...
- happiness[国家集训队2011(吴确)]
[试题来源] 2011中国国家集训队命题答辩 [问题描述] 高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友.这学期要分文理科了,每个同学对于选择文科 ...
- 深入理解Spring MVC 思想
目录 一.前言二.spring mvc 核心类与接口三.spring mvc 核心流程图 四.spring mvc DispatcherServlet说明 五.spring mvc 父子上下文的说明 ...
- Android融合推送MixPush SDK集成多家推送平台,共享系统级推送,杀死APP也能收到推送
消息推送是App运营的重要一环,为了优化消息推送成功率,降低电量和流量消耗,系统级的推送服务显得尤为重要.小米和魅族由此推出了自家的推送平台,在MIUI和Flyme上共享系统级推送服务,让APP在被杀 ...
- ABP+AdminLTE+Bootstrap Table权限管理系统第九节--AdminLTE模板页搭建
AdminLTE 官网地址:https://adminlte.io/themes/AdminLTE/index2.html 首先去官网下载包下来,然后引入项目. 然后我们在web层添加区域Admin以 ...
- 67. Add Binary【LeetCode】
67. Add Binary Given two binary strings, return their sum (also a binary string). For example,a = &q ...
- String对象常用的一些方法
anchor() 创建 HTML 锚. big() 用大号字体显示字符串. blink() 显示闪动字符串. bold() 使用粗体显示字符串. charAt() 返回在指定位置的字符. charCo ...
- 前端到后台ThinkPHP开发整站(4)
今晚继续我的这个项目的开发,今晚也是写的不多,主要写了一个菜单管理功能的CURD方法,前端界面还没有进行编写. 菜单管理Model层的代码: <?php namespace Common\Mod ...