一、问题的发现与提出

  在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1:

代码段1:

class A:def __init__(self):print "enter A"print "leave A"class B(A):def __init__(self):print "enter B"A.__init__(self)print "leave B">>> b = B()enter Benter Aleave Aleave B

即,使用非绑定的类方法(用类名来引用的方法),并在参数列表中,引入待绑定的对象(self),从而达到调用父类的目的。

  这样做的缺点是,当一个子类的父类发生变化时(如类B的父类由A变为C时),必须遍历整个类定义,把所有的通过非绑定的方法的类名全部替换过来,例如代码段2,

代码段2:

class B(C):    # A --> Cdef __init__(self):print "enter B"C.__init__(self) # A --> Cprint "leave B"

  如果代码简单,这样的改动或许还可以接受。但如果代码量庞大,这样的修改可能是灾难性的。

  因此,自Python 2.2开始,Python添加了一个关键字super,来解决这个问题。下面是Python 2.3的官方文档说明:

super(type[, object-or-type])

Return the superclass of type. If the second argument is omitted the super object
  returned is unbound. If the second argument is an object, isinstance(obj, type) 
  must be true. If the second argument is a type, issubclass(type2, type) must be 
  true. super() only works for new-style classes.

A typical use for calling a cooperative superclass method is:

class C(B):
       def meth(self, arg):
           super(C, self).meth(arg)

New in version 2.2.

  从说明来看,可以把类B改写如代码段3:

代码段3:

class A(object):    # A must be new-style classdef __init__(self):print "enter A"print "leave A"class B(C):     # A --> Cdef __init__(self):print "enter B"super(B, self).__init__()print "leave B"

  尝试执行上面同样的代码,结果一致,但修改的代码只有一处,把代码的维护量降到最低,是一个不错的用法。因此在我们的开发过程中,super关键字被大量使用,而且一直表现良好。

  在我们的印象中,对于super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象(通过某种方式,一直没有考究是什么方式,惭愧),然后“被转换”的类A对象调用自己的__init__函数。考虑到super中只有指明子类的机制,因此,在多继承的类定义中,通常我们保留使用类似代码段1的方法。

  有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如代码段4:

代码段4:

 1 class A(object):
 2   def __init__(self):
 3    print "enter A"
 4    print "leave A"
 5
 6  class B(object):
 7   def __init__(self):
 8    print "enter B"
 9    print "leave B"
10
11  class C(A):
12   def __init__(self):
13    print "enter C"
14    super(C, self).__init__()
15    print "leave C"
16
17  class D(A):
18   def __init__(self):
19    print "enter D"
20    super(D, self).__init__()
21    print "leave D"
22  class E(B, C):
23   def __init__(self):
24    print "enter E"
25    B.__init__(self)
26    C.__init__(self)
27    print "leave E"
28
29  class F(E, D):
30   def __init__(self):
31    print "enter F"
32    E.__init__(self)
33    D.__init__(self)
34    print "leave F"

f = F() result:

enter Fenter Eenter Bleave Benter Center Denter Aleave Aleave Dleave Cleave Eenter Denter Aleave Aleave Dleave F
 enter Fenter Eenter Bleave Benter Center Denter Aleave Aleave Dleave Cleave Eenter Denter Aleave Aleave Dleave 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改写为:

代码段9:

class A(object):def __init__(self):print "enter A"super(A, self).__init__()  # newprint "leave A"class B(object):def __init__(self):print "enter B"super(B, self).__init__()  # newprint "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__()  # changeprint "leave E"class F(E, D):def __init__(self):print "enter F"super(F, self).__init__()  # changeprint "leave F"
class A(object):def __init__(self):print "enter A"super(A, self).__init__()  # newprint "leave A"class B(object):def __init__(self):print "enter B"super(B, self).__init__()  # newprint "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__()  # changeprint "leave E"class F(E, D):def __init__(self):print "enter F"super(F, self).__init__()  # changeprint "leave F"

f = F() result:

 enter Fenter Eenter Benter Center Denter Aleave Aleave Dleave Cleave Bleave Eleave F

  明显地,F的初始化不仅完成了所有的父类的调用,而且保证了每一个父类的初始化函数只调用一次。

  再看类结构:

    object/   \/      A|     /   \B-1  C-2   D-2\   /    /E-1    /\  /F

E-1,D-2是F的父类,其中表示E类在前,即F(E,D)。

所以初始化顺序可以从类结构图来看出 : F->E->B -->C --> D --> A

由于C,D有同一个父类,因此会先初始化D再是A。

三、延续的讨论

  我们再重新看上面的类体系图,如果把每一个类看作图的一个节点,每一个从子类到父类的直接继承关系看作一条有向边,那么该体系图将变为一个有向图。不能发现mro的顺序正好是该有向图的一个拓扑排序序列。

  从而,我们得到了另一个结果——Python是如何去处理多继承。支持多继承的传统的面向对象程序语言(如C++)是通过虚拟继承的方式去实现多继承中父类的构造函数被多次调用的问题,而Python则通过mro的方式去处理。

  但这给我们一个难题:对于提供类体系的编写者来说,他不知道使用者会怎么使用他的类体系,也就是说,不正确的后续类,可能会导致原有类体系的错误,而且这样的错误非常隐蔽的,也难于发现。

四、小结

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

转载于:https://www.cnblogs.com/xiaoerlang/p/3460939.html

python super()(转载)相关推荐

  1. [转载] python super详解

    参考链接: Python super() 说到 super, 大家可能觉得很简单呀,不就是用来调用父类方法的嘛.如果真的这么简单的话也就不会有这篇文章了,且听我细细道来.? 约定 在开始之前我们来约定 ...

  2. Python super 函数 - Python零基础入门教程

    目录 一.Python super 函数简介 二.Python super 函数语法 三.Python super 函数使用 1.案例一 2.案例二: 四.猜你喜欢 零基础 Python 学习路线推荐 ...

  3. python super 变参数问题(五)

    这是Python多层继承的一个特例,祖父,父亲,儿子都有 draw 方法,那么经过多次继承后, 如何对于不同层次的方法传递参数呢,可以看这篇文章python super 理解(四) 如何对于不同层次的 ...

  4. python super()函数(用来调用父类方法)

    如图,super()用于调用当前类Canvas的父类QWidget的size()方法: 参考文章1:Python super 详解 参考文章2:Python super() 函数

  5. pythonsuper函数_怎么使用python super函数调用父类

    怎么使用python super函数调用父类 发布时间:2020-11-19 09:19:47 来源:亿速云 阅读:93 作者:小新 小编给大家分享一下怎么使用python super函数调用父类,希 ...

  6. python super 多重继承_解决python super()调用多重继承函数的问题

    当类间继承关系很简单时,super()的使用很简单. class A(object): def __init__(self): print('a') class B(A): def __init__( ...

  7. 设计一个矩形类rectangle_使用Python super()为您的类增强

    虽然Python不仅仅是一种面向对象的语言,但它足够灵活,功能强大,足以让您使用面向对象的范例构建应用程序.Python实现这一目标的方法之一是支持继承,它与之相关super(). 在本教程中,您将了 ...

  8. Python super钻石继承

    1.   Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object):def __init__ ...

  9. python super()方法的作用_详解python的super()的作用和原理

    Python中对象方法的定义很怪异,第一个参数一般都命名为self(相当于其它语言的this),用于传递对象本身,而在调用的时候则不必显式传递,系统会自动传递.uz0免费资源网 今天我们介绍的主角是s ...

  10. python super

    原文地址:http://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html python super() 一.问题的发现与提出 在Py ...

最新文章

  1. HDU 2094 产生冠军 (map容器)
  2. electron 项目的搭建方式,借助 node 和 npm
  3. [python、flask] - POST请求
  4. Javascript 检测 页面是否在iframe中
  5. Android 说说亮屏锁和键盘锁
  6. 数学学习过程中的感悟(一)
  7. python 上下文管理器、 else 块、@contextmanager
  8. 干货篇:创业对待数据挖掘要注意这5点
  9. Netty之Bootstrap详解
  10. 服务器和数据库基本知识
  11. 凯撒密码加密算法python_想偷WiFi?万能钥匙不行?试试python一键破解!|wifi|python|profile|算法|无线网卡...
  12. 斜杠(右斜杠)【/】 与 反斜杠(右斜杠)【\】
  13. linux mud 游戏,150919神奇的mud
  14. 2020Java后端开发面试题总结(春招+秋招+社招)
  15. MySQL 异步查询提高查询速度
  16. Gluster集群管理小分析
  17. win安装夜神安卓模拟器
  18. 机器学习强基计划4-2:通俗理解极大似然估计和极大后验估计+实例分析
  19. 电磁炉全国产化电子元件推荐方案
  20. 传统的不确定性量化数值方法

热门文章

  1. java thread_Java(多线程Thread)
  2. ACM思维题训练 Section A
  3. 图书馆管理系统用户端心得
  4. [链表]---链表中环的入口节点
  5. Python面试题目--汇总
  6. Linux 嵌入式启动以及优化(Z)
  7. pytorch实现文本分类_使用变形金刚进行文本分类(Pytorch实现)
  8. ces测试数据测试ces测试数据测试ces测试数据测试ces测试数据测试ces测试数据测试ces测试数据测试ces测试数据测试再次测试
  9. SCSI、FC、iSCSI三大协议概述
  10. 将1bpp的bmp图像存储为1bpp或者2bpp的tiff格式