1.新式类中多继承----经典的钻石问题
一个类可以继承多个类,一个类也可以被多个类继承,被继承的类又称为父类,基类或超类;继承父类的类叫子类或派生类;
一个类在继承多个类时,默认是就近原则,顺序就是在新式类中是广度优先原则;在经典类中是深度优先原则;
(python3版本中的类都是新式类,python2版本中的类既包括新式类也有经典类,区别是python2版本中的新式类都是继承自Object的)
1. 先看一个多继承的例子:
class A(): def func(self): print("A") class B(A): def func(self): print('B') class C(A): def func(self): print("C") class D(B,A): def func(self): print('D') d=D() d.func()
运行结果:
这很明显,虽然D这个类继承了B C但是当实例化D时,对象调用func()方法,但是我自己的类中就有的话当然用自己的~
2. 当D()这个类中没有func()方法呢,会调用谁呢~
class A(): def func(self): print("A") class B(A): def func(self): print('B') class C(A): def func(self): print("C") class D(B,C): pass # def func(self): # print('D') d=D() d.func()
运行结果:
因为D()自己的类中没有func()方法,就去父类中找,原则是就近原则,所以会去B这个父类中查找
3.当B()类中也没有func()方法呢,又会先调用谁呢~
class A(): def func(self): print("A") class B(A): pass # def func(self): # print('B') class C(A): def func(self): print("C") class D(B,C): pass # def func(self): # print('D') d=D() d.func()
运行结果:
由于D()这个类继承自B C 自己的类中没有func()方法,就去父类B中找,但是B中也没有func()方法,虽然B继承自A 但是此时并没有一直沿着B去找A 这个爷爷类,而是去找了D的另一个父类C ,然后调用C类中的func()方法(这里之所以不去找A是因为广度优先遍历,知道A这个节点 后续也会由C到达A 所以走到B之后直接走的C)(其实最后当C中也找不到相应的方法时就去A中查找;)
4. 当C()这个类中也没有func()方法时,就去调用A中的方法:
class A(): def func(self): print("A") class B(A): pass # def func(self): # print('B') class C(A): pass # def func(self): # print("C") class D(B,C): pass # def func(self): # print('D') d=D() d.func()
运行结果:
上面就是经典的多继承中的钻石问题(是这样叫吧)画一下图,表示继承顺序:
为啥走到B之后不先走A 而是先走C呢 就是因为它知道A这个节点,后续通过C也能到达
其实我们可以根据子类名.mro()方法查看该子类调用父类时的继承顺序:
print(D.mro())
2.新式类中多继承----小乌龟
class F(): def func(self): print("F") class A(F): def func(self): print("A") class E(F): def func(self): print("E") class B(A): def func(self): print('B') class C(E): pass def func(self): print("C") class D(B,C): pass def func(self): print('D') d=D() d.func()
我们按照上面的方法依次注释掉类中相应的方法,然后观察继承顺序:
发现是 D----> B----->A--->C-----E----->F
画一下流程图:
首先当D中有相应的方法时,我们肯定优先调用D类中的方法,如果D中没有相应的方法我们才去D的父类中去查找相应的方法,B C 都是父类,但是按照广度优先,应该先走B 然后当B中没有相应的方法时,应该是走B的父类A 还是走D的父类C呢,其实是应该走A的,因为A如果这个时刻不走的话,走其他节点是无论如何都到不了A的,所以先走A ,然后接下来是走A的父类F还是走D的父类C呢,其实应该走C 因为你发现这个时候不走F 后面仍然有其他途径可以到达F呀,所以此时先走C 然后走E 最后走F
以上就是新式类中的多继承的继承顺序,就是默认就近原则,顺序是广度优先;而python3中的类都是新式类;python2中既有新式类(继承自Object)也有经典类;
那对于经典类的多继承继承顺序应该是深度优先!!
3.经典类中钻石问题:
经典类中多继承是深度优先,就是在找父类的过程中是沿着一条路走到黑,深度优先
4.经典类中小乌龟问题:
5.多继承中的super():
子类名.mro()方法可以查看子类继承父类的继承顺序,只可以在新式类中使用;super()方法只可以在python3中的新式类中使用!!
单继承时,我们提到过在子类中的初始化方法__init__(self,子类中的对象属性)中如果还想使用父类中的相关属性,有两种方法:
1. 父类名.__init__(self,父类中相应的对象属性) 这里的self对象就是代表子类实例化的对象,是必须要传的参数;
2.super().__init__(父类中的对象属性),这时候super().__init__()这个方法中是不需要传入self参数的(因为super()__init__(父类中的对象属性)中的super()其实就是super(子类名,self))
单继承时如果自己实现了一个与父类重名的方法,我们如果想在子类的该重名方法中使用父类的该重名方法 也有两种方法:
1. 父类名.方法(self,方法中的其他参数),这里的self参数也是必须要传的;
2.super().方法名(方法中的其他参数) 这时候是不需要传self参数------》这里是在子类的重名方法内这样写(子类方法中的super()是没有参数的 ,其实是super(子类名,self)省略了而已),当然也可以在类外部写:super(子类名,子类实例化对象).方法名(方法的其他参数)
现在要讲一种在多继承中使用super() 代表的其实不是父类了,在多继承中,super()的本质已经不是直接找父类,而是根据调用者的节点位置的广度优先顺序查找的,谁在下一个顺序,谁就是super()
class A(): def func(self): print("A") class B(A): def func(self): super().func() print("B") class C(A): def func(self): super().func() print("C") class D(B,C): def func(self): super().func() print("D") d=D() d.func()
运行结果:
原因就是,super()的本质不是直接找父类,而是根据调用者(这里是d=D() 是D类的实例化对象)节点位置的广度优先顺序(上面已经分析了 在新式类(因为super()只用在新式类)中多继承的继承顺序是广度优先,应该是D,B,C,A)所以首先d.func()执行的D类中定义的func()函数,先执行里面的super().func(),super()指的是B 也就是去执行B.func() 发现B.func()中 也有super().func() 这里的super()指的不是父类,而是调用者节点位置的广度优先顺序,B之后应该是C节点,所以B.func()这里的super().func()就是去执行C.func() ,C.func()函数中的super().func()指的就是A.func() 所以先输出A,,然后C 然后B 最后D;