开始之前,先出一道题:

 #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原理剖析的更多相关文章

  1. Python中的super函数,你熟吗?

    摘要:经常有朋友问,学 Python 面向对象时,翻阅别人代码,会发现一个 super() 函数,那这个函数的作用到底是什么? 本文分享自华为云社区<Python中的super函数怎么学,怎么解 ...

  2. python基础----多态与多态性、super函数用法、继承原理

    一.多态与多态性                                                                        ㈠多态: 多态指的是一类事物有多种形态, ...

  3. 由Python的super()函数想到的

    python-super *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !im ...

  4. Python super() 函数的概念和例子

    概念: super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO).重 ...

  5. Python super() 函数

    super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果重定义某个方法,该方法会覆盖父类的同名方法,但有时 ...

  6. python super()函数详解

    引言: 在类的多继承使用场景中,重写父类的方法时,可能会考虑到需要重新调用父类的方法,所以super()函数就是比较使用也很必要的解决方法: 文章来源: http://www.cnblogs.com/ ...

  7. Python面试题之Super函数

    这是个高大上的函数,在python装13手册里面介绍过多使用可显得自己是高手 23333. 但其实他还是很重要的. 简单说, super函数是调用下一个父类(超类)并返回该父类实例的方法. 这里的下一 ...

  8. python 中 super函数的使用

    转载地址:http://python.jobbole.com/86787/ 1.简单的使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我 ...

  9. Python3中的super()函数详解

    关于Python3中的super()函数 我们都知道,在Python3中子类在继承父类的时候,当子类中的方法与父类中的方法重名时,子类中的方法会覆盖父类中的方法, 那么,如果我们想实现同时调用父类和子 ...

随机推荐

  1. hdu_1506:Largest Rectangle in a Histogram 【单调栈】

    题目链接 对栈的一种灵活运用吧算是,希望我的注释写的足够清晰.. #include<bits/stdc++.h> using namespace std; typedef long lon ...

  2. Jmeter之处理session、cookie以及如何做关联

    具体描述问题之前,我们先了解下session.cookie session.cookie的概念 1.session是放在服务器上的,过期与否取决于服务期的设定,cookie是存在客户端的,过去与否可以 ...

  3. centos 7 下面安装oracle 11g r2 过程分享

    本人对LINUX等很多还不熟悉,如果有不对的地方还请各位指正.谢谢. 打算学习下ORACLE,RMAN备份与还原功能,所以安装了虚拟机,用的是centos7 X86_64-1611版本,oracle用 ...

  4. TCP 滑动窗口

    滑动窗口协议 流量控制方法 PUSH 慢启动   隔一个报文段确认"的策略实际就是因为 delayed ack,同时接收到两个待确认的ACK包时,就立即发送确认包.   滑动窗口实例   解 ...

  5. KNN算法--python实现

    邻近算法 或者说K最近邻(kNN,k-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一.所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代 ...

  6. Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.

    我用String代替了链表显示,本题的大意是每k个进行逆序处理,剩下的不够k个的就按照原顺序保留下来. public class ReverseNodes { public static void m ...

  7. PXE+kickstart无人值守安装CentOS 6

    本文目录: 1.1 PXE说明 1.2 PXE流程 1.3 部署环境说明 1.4 部署DHCP 1.5 部署TFTP 1.6 提供pxe的bootloader和相关配置文件 1.7 利用原版安装镜像获 ...

  8. mysql 查询性能优化第一章 为什么查询速度会慢

    一 为什么查询速度会慢 在尝试编写快速的查询之前,咱们需要清除一点,真正重要的是响应时间.如果把查询看成是一个任务,那么它由一系列子任务组成,每个子任务都会消耗一定的时间.如果要有 优化查询,实际上要 ...

  9. [NOIP 2010]饮水入城 搜索+贪心

    考试的时候写了个dfs找出来了,最后处理的时候想到了贪心,但是正确性没有想通.然后想了想动规,也没想通.最后没办法,用状态的话用了个状压,弄了40分. 正解是bfs+贪心.Dfs也有过的. 下面题解引 ...

  10. Java框架概述

    一.框架的意义 1.什么是框架? 框架就是一些类和接口的集合,通过这些类和接口协调来完成一系列的程序实现.有了框架,我们就可以集中精力进行业务逻辑的开发而不用去关心它的技术实现以及一些辅助的业务逻辑. ...