python学习笔记 day24 多继承

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;

talk is cheap,show me the code
原文地址:https://www.cnblogs.com/xuanxuanlove/p/9671593.html