zoukankan      html  css  js  c++  java
  • 多继承带来的菱形问题

    大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”),菱形其实就是对下面这种继承结构的形象比喻

    1586423053645

    这种继承结构下导致的问题称之为菱形问题:如果A中有一个方法,B和/或C都重写了该方法,而D没有重写它,那么D继承的是哪个版本的方法:B的还是C的?如下所示

    class A(object):
        def test(self):
            print('from A')


    class B(A):
        def test(self):
            print('from B')


    class C(A):
        def test(self):
            print('from C')


    class D(B,C):
        pass


    obj = D()
    obj.test() # 结果为:from B

    一、继承原理(MRO)

    python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,如下

    >>> D.mro() # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法
    [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

    python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

    1.子类会先于父类被检查
    2.多个父类会根据它们在列表中的顺序被检查
    3.如果对下一个类存在两个合法的选择,选择第一个父类

    所以obj.test()的查找顺序是,先从对象obj本身的属性里找方法test,没有找到,则参照属性查找的发起者(即obj)所处类D的MRO列表来依次检索,首先在类D中未找到,然后再B中找到方法test

    1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
    2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,

    二、深度和广度的优先级

    非菱形结构:

    参照下述代码,多继承结构为非菱形结构,此时,会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

    1586423281436

    class E:
        def test(self):
            print('from E')


    class F:
        def test(self):
            print('from F')


    class B(E):
        def test(self):
            print('from B')


    class C(F):
        def test(self):
            print('from C')


    class D:
        def test(self):
            print('from D')


    class A(B, C, D):
        # def test(self):
        #     print('from A')
        pass


    print(A.mro())
    '''
    [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
    '''

    obj = A()
    obj.test() # 结果为:from B
    # 可依次注释上述类中的方法test来进行验证

    菱形结构:

    经典类————》python2

    如果继承关系为菱形结构,那么经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先1586423326835

    class G: # 在python2中,未继承object的类及其子类,都是经典类
       def test(self):
            print('from G')

    class E(G):
        def test(self):
            print('from E')

    class F(G):
        def test(self):
            print('from F')

    class B(E):
        def test(self):
            print('from B')

    class C(F):
        def test(self):
            print('from C')

    class D(G):
        def test(self):
            print('from D')

    class A(B,C,D):
        # def test(self):
        #     print('from A')
        pass

    obj = A()
    obj.test() # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object
    # 可依次注释上述类中的方法test来进行验证,注意请在python2.x中进行测试

    新式类————》python3

    1586423387815

    class G(object):
        def test(self):
            print('from G')

    class E(G):
        def test(self):
            print('from E')

    class F(G):
        def test(self):
            print('from F')

    class B(E):
        def test(self):
            print('from B')

    class C(F):
        def test(self):
            print('from C')

    class D(G):
        def test(self):
            print('from D')

    class A(B,C,D):
        # def test(self):
        #     print('from A')
        pass

    obj = A()
    obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object
    # 可依次注释上述类中的方法test来进行验证
    总结:
    多继承到底要不用???
    要用,但是规避几点问题
    1、继承结构尽量不要过于复杂
    2、推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系
  • 相关阅读:
    2021-4-1 日报博客
    2021-3-31 日报博客
    2021-3-30 日报博客
    2021-3-29 日报博客
    2021-3-27 周报博客
    java
    周末总结六
    java
    java
    java
  • 原文地址:https://www.cnblogs.com/bailongcaptain/p/12670533.html
Copyright © 2011-2022 走看看