深入super

一下内容引用自:http://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html,写的挺好的。

代码段3
class A(object):    # A must be new-style class
def __init__(self):
print "enter A"
print "leave A" class B(C): # A --> C
def __init__(self):
print "enter B"
super(B, self).__init__()
print "leave B"
在我们的印象中,对于super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象,然后“被转换”的类A对象调用自己的__init__函数。

有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如下
代码段4:
class A(object):
def __init__(self):
print "enter A"
print "leave A" class B(object):
def __init__(self):
print "enter B"
print "leave B" class C(A):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C" class D(A):
def __init__(self):
print "enter D"
super(D, self).__init__()
print "leave D"
class E(B, C):
def __init__(self):
print "enter E"
B.__init__(self)
C.__init__(self)
print "leave E" class F(E, D):
def __init__(self):
print "enter F"
E.__init__(self)
D.__init__(self)
print "leave F"

f = F() ,结果如下:

enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F

明显地,类A和类D的初始化函数被重复调用了2次,这并不是我们所期望的结果!我们所期望的结果是最多只有类A的初始化函数被调用2次——其实这是多继承的类体系必须面对的问题。我们把代码段4的类体系画出来,如下图:

object
   |       \
   |        A
   |      / |
   B  C  D
    \   /   |
      E    |
        \   |
          F

按我们对super的理解,从图中可以看出,在调用类C的初始化函数时,应该是调用类A的初始化函数,但事实上却调用了类D的初始化函数。好一个诡异的问题!

也就是说,mro中记录了一个类的所有基类的类类型序列。查看mro的记录,发觉包含7个元素,7个类名分别为:

F E B C D A object

  从而说明了为什么在C.__init__中使用super(C, self).__init__()会调用类D的初始化函数了。 ???

  我们把代码段4改写为:

代码段5:
class A(object):
def __init__(self):
print "enter A"
super(A, self).__init__() # new
print "leave A" class B(object):
def __init__(self):
print "enter B"
super(B, self).__init__() # new
print "leave B" class C(A):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C" class D(A):
def __init__(self):
print "enter D"
super(D, self).__init__()
print "leave D"
class E(B, C):
def __init__(self):
print "enter E"
super(E, self).__init__() # change
print "leave E" class F(E, D):
def __init__(self):
print "enter F"
super(F, self).__init__() # change
print "leave F"

f = F(),执行结果:

enter F
enter E
enter B
enter C
enter D
enter A
leave A
leave D
leave C
leave B
leave E
leave F 可见,F的初始化不仅完成了所有的父类的调用,而且保证了每一个父类的初始化函数只调用一次。

小结

  1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
       产生了一个super对象;
  2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
  3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
  4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
       只调用一次(如果每个类都使用super);
  5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
       个父类函数被调用多次。

一些更深入的问题:各位可以看到,print F.__mro__时发现里面元素的顺序是 F E B C D A object,这就是F的基类查找顺序,至于为什么是这样的顺序,以及python内置的多继承顺序是怎么实现的,这涉及到mro顺序的实现,python 2.3以后的版本中是采用的一个叫做C3的算法,在下篇博客中进行介绍。

 

python 多继承(新式类) 三的更多相关文章

  1. Python 多继承(新式类) 的mro算法

    转载自:http://www.cnblogs.com/panyinghua/p/3283831.html mro即method resolution order,主要用于在多继承时判断调的属性的路径( ...

  2. Python之面向对象新式类和经典类

    Python之面向对象新式类和经典类 新式类和经典类的继承原理: 在Python3中,就只有新式类一种了. 先看Python3中新式类: 类是有继承顺序的: Python的类是可以继承多个类的,也就是 ...

  3. Python基础:新式类的属性访问

    一.概述 二.准备工作 1.讨论对象 2.名词解释 三.实例绑定的属性访问 1.获取属性 一般规则 参考源码 示例验证 2.设置属性 一般规则 参考源码 示例验证 3.删除属性 一般规则 参考源码 示 ...

  4. python(十四)新式类和旧式类

    这里有个重要概念呢在下面那个链接 http://blog.csdn.net/zimou5581/article/details/53053775 http://www.cnblogs.com/btch ...

  5. python单例模式控制成只初始化一次,常规型的python单例模式在新式类和经典类中的区别。

    单例模式的写法非常多,但常规型的单例模式就是这样写的,各种代码可能略有差异,但核心就是要搞清楚类属性 实例属性,就很容易写出来,原理完全一模一样. 如下: 源码: class A(object): d ...

  6. Python - 面向对象编程 - 新式类和旧式类

    object object 是 Python 为所有对象提供的父类,默认提供一些内置的属性.方法:可以使用 dir 方法查看 新式类 以 object 为父类的类,推荐使用 在 Python 3.x ...

  7. python中的新式类与旧式类

    在python2中,有新式类与旧式类的区别: 首先创建一个类: class Sb(object): pass 如果创建时继承自object,说明这是一个新式类,不写object,说明是一个旧式类: 那 ...

  8. 多任务-python实现-继承Thread类,单独编写一个类(2.1.2)

    @ 目录 1.thread类 1.thread类 threding代码实现 import threading import time class MyThread(threading.Thread): ...

  9. 【python】-- 类的多继承、经典类、新式类

    继承知识点补充 在python还支持多继承,但是一般我们很少用,有些语言干脆就不支持多继承,有多继承,就会带来两个概念,经典类和新式类. 一.多继承 之前我们都是讲的单继承,那么什么是多继承呢?说白了 ...

  10. python开发学习-day07(面向对象之多态、类的方法、反射、新式类and旧式类、socket编程)

    s12-20160227-day07 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...

随机推荐

  1. 阿里云数据库产品HybridDB简介——OLAP数据库,支持行列混合存储,基于数据库Greenplum的开源版本,并且吸收PostgreSQL精髓

    为什么会有HybridDB的诞生?它经历了怎样的研发历程?它的应用场景和情况是怎样的?带着这些问题,InfoQ对阿里云的数据库专家兼Postgres中国社区/中国用户会主席萧少聪先生进行了采访,以下文 ...

  2. [原创]java对word文档的在线打开

    一.材料准备 百度一下:PageOffice,从官网下载PageOffice for Java.压缩包文件: 二. 实现步骤: 1. 打开“集成文件”目录,拷贝“WEB-INF\lib”目录中的pag ...

  3. 修改SO

    1.如果在jni中定义的是int型的数据,比如: JNIEXPORT jint JNICALL Java_com_ggndktest1_JniGg_getCoin (JNIEnv * env, job ...

  4. CodeForces - 840D:(主席树求出现区间出现次数大于某值的最小数)

    Once, Leha found in the left pocket an array consisting of n integers, and in the right pocket q que ...

  5. CodeForces669E:Little Artem and Time Machine(CDQ分治)(或者用map+树状数组优美地解决)

    Little Artem has invented a time machine! He could go anywhere in time, but all his thoughts of cour ...

  6. [SHOI 2017] 寿司餐厅

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=4873 [算法] 注意到题目中的限制条件可表述为 : 若选择区间[L , R] , 则 ...

  7. C++之自己实现的String类全部

    一:回顾 (1)c++中的string类是在面试中和笔试中经常考的题目: 工程代码免费下载 string类的自行实现 (2)c++中的string类和fstream类合起来是处理外部数据的利器: (3 ...

  8. zynq交叉编译环境设置

    环境准备 Vmware 虚拟机:ubuntu14.04 下载安装文件 环境设置 按照上面地址下载 xilinx-2011.09-50-arm-xilinx-linux-gnueabi.bin 将das ...

  9. python序列化之pickle,json,shelve

    模块 支持方法 说明 json dumps/dump loads/load 只能处理基本数据类型: 用于多种语言间的数据传输: pickle dumps/dump loads/load 支持pytho ...

  10. c++的const总结(转)

    为什么使用const?采用符号常量写出的代码更容易维护:指针常常是边读边移动,而不是边写边移动:许多函数参数是只读不写的.const最常见用途是作为数组的界和switch分情况标号(也可以用枚举符代替 ...