python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object): def __init__(self): print “Base init” 则普通方法以下 class Leaf(Base): def __init__(self): Base.__init__(self) print “Leaf init” super方法以下 class Leaf(Base): def __init__(self): super(Leaf, self).__init__() print “Leaf init” 在上面的简单场景下,两种方法的效果一致: >>> leaf = Leaf() Base init Leaf init 2. 钻石继承遇到的难题 当咱们来到钻石继承场景时,咱们就遇到了一个难题: 若是咱们仍是使用普通方法调用父类成员,代码以下: class Base(object): def __init__(self): print “Base init” class Medium1(Base): def __init__(self): Base.__init__(self) print “Medium1 init” class Medium2(Base): def __init__(self): Base.__init__(self) print “Medium2 init” class Leaf(Medium1, Medium2): def __init__(self): Medium1.__init__(self) Medium2.__init__(self) print “Leaf init” 当咱们生成Leaf对象时,结果以下: >>> leaf = Leaf() Base init Medium1 init Base init Medium2 init Leaf init 能够看到Base被初始化了 两次 !这是因为Medium1和Medium2各自调用了Base的初始化函数致使的。 3. 各语言的解决方法 钻石继承中,父类被屡次初始化是个很是难缠的问题,咱们来看看其余各个语言是如何解决这个问题的: 3.1. C++ C++使用虚拟继承来解决钻石继承问题。 Medium1和Medium2虚拟继承Base。当生成Leaf对象时,Medium1和Medium2并不会自动调用虚拟基类Base的构造函数,而须要由Leaf的构造函数显式调用Base的构造函数。 3.2. Java Java禁止使用多继承。 Java使用单继承+接口实现的方式来替代多继承,避免了钻石继承产生的各类问题。 3.3. Ruby Ruby禁止使用多继承。 Ruby和Java同样只支持单继承,但它对多继承的替代方式和Java不一样。Ruby使用Mixin的方式来替代,在当前类中mixin入其余模块,来作到代码的组装效果。 3.4. Python Python和C++同样,支持多继承的语法。但Python的解决思路和C++彻底不同,Python是的用就是super 咱们把第2章的钻石继承用super重写一下,看一下输出结果 class Base(object): def __init__(self): print “Base init” class Medium1(Base): def __init__(self): super(Medium1, self).__init__() print “Medium1 init” class Medium2(Base): def __init__(self): super(Medium2, self).__init__() print “Medium2 init” class Leaf(Medium1, Medium2): def __init__(self): super(Leaf, self).__init__() print “Leaf init” 咱们生成Leaf对象: >>> leaf = Leaf() Base init Medium2 init Medium1 init Leaf init 能够看到整个初始化过程符合咱们的预期,Base只被初始化了1次。并且重要的是,相比原来的普通写法,super方法并无写额外的代码,也没有引入额外的概念 4. super的内核:mro 要理解super的原理,就要先了解mro。mro是method resolution order的缩写,表示了类继承体系中的成员解析顺序。 在python中,每一个类都有一个mro的类方法。咱们来看一下钻石继承中,Leaf类的mro是什么样子的: >>> Leaf.mro() [Leaf, Medium1, Medium2, Base] 能够看到mro方法返回的是一个祖先类的列表。Leaf的每一个祖先都在其中出现一次,这也是super在父类中查找成员的顺序。 经过mro,python巧妙地将多继承的图结构,转变为list的顺序结构。super在继承体系中向上的查找过程,变成了在mro中向右的线性查找过程,任何类都只会被处理一次。 经过这个方法,python解决了多继承中的2大难题: 1. 查找顺序问题。从Leaf的mro顺序能够看出,若是Leaf类经过super来访问父类成员,那么Medium1的成员会在Medium2以前被首先访问到。若是Medium1和Medium2都没有找到,最后再到Base中查找。 2. 钻石继承的屡次初始化问题。在mro的list中,Base类只出现了一次。事实上任何类都只会在mro list中出现一次。这就确保了super向上调用的过程当中,任何祖先类的方法都只会被执行一次。 至于mro的生成算法,能够参考这篇wiki:https://en.wikipedia.org/wiki/C3_linearization 5. super的具体用法 咱们首先来看一下python中的super文档 >>> help(super) Help on class super in module __builtin__: class super(object) | super(type, obj) -> bound super object; requires isinstance(obj, type) | super(type) -> unbound super object | super(type, type2) -> bound super object; requires issubclass(type2, type) 光从字面来看,这能够算是python中最语焉不详的帮助文档之一了。甚至里面还有一些术语误用。那super究竟应该怎么用呢,咱们重点来看super中的第1和第3种用法 5.1. super(type, obj) 当咱们在Leaf的__init__中写这样的super时: class Leaf(Medium1, Medium2): def __init__(self): super(Leaf, self).__init__() print “Leaf init” super(Leaf, self).__init__()的意思是说: 获取self所属类的mro, 也就是[Leaf, Medium1, Medium2, Base] 从mro中Leaf右边的一个类开始,依次寻找__init__函数。这里是从Medium1开始寻找 一旦找到,就把找到的__init__函数绑定到self对象,并返回 从这个执行流程能够看到,若是咱们不想调用Medium1的__init__,而想要调用Medium2的__init__,那么super应该写成:super(Medium1, self)__init__() 5.2. super(type, type2) 当咱们在Leaf中写类方法的super时: class Leaf(Medium1, Medium2): def __new__(cls): obj = super(Leaf, cls).__new__(cls) print “Leaf new” return obj super(Leaf, cls).__new__(cls)的意思是说: 获取cls这个类的mro,这里也是[Leaf, Medium1, Medium2, Base] 从mro中Leaf右边的一个类开始,依次寻找__new__函数 一旦找到,就返回“ 非绑定 ”的__new__函数 因为返回的是非绑定的函数对象,所以调用时不能省略函数的第一个参数。这也是这里调用__new__时,须要传入参数cls的缘由 一样的,若是咱们想从某个mro的某个位置开始查找,只须要修改super的第一个参数就行 6. 小结 至此,咱们讲解了和super相关的用法及原理,小结一下咱们讲过的内容有: python调用父类成员共有2种方法:普通方法,super方法 在钻石继承中,普通方法会遇到Base类两次初始化的问题 简述了其余语言对这个问题的解决方法,并用实例展现了python使用super能够解决此问题 在讲super具体用法前,先讲了super的内核:mro的知识和原理 讲解了super两种主要的用法及原理