• Python正课74 —— 多继承带来的菱形问题


    本文内容皆为作者原创,如需转载,请注明出处:https://www.cnblogs.com/xuexianqi/p/12669925.html

    一:菱形问题

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

    LXWTT

    class A(object):
        def test(self):
            print('from A')
        pass
    
    
    class B(A):
        def test(self):
            print('from B')
        pass
    
    
    class C(A):
        def test(self):
            print('from C')
        pass
    
    
    class D(C, B):
        # def test(self):
        #     print('from D')
        pass
    
    
    # print(D.mro())  # 类D以及类D的对象访问属性都是参照该类的mro列表
    # 输出:[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    
    obj = D()
    obj.test()        # from C
    
    print(D.test)       # <function C.test at 0x039D84A8>
    
    print(C.mro())  # 类C以及类C的对象访问属性都是参照该类的mro列表
    # 输出:[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    
    c = C()
    c.test()        # from C
    

    总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro

    python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
    
    1.子类会先于父类被检查
    2.多个父类会根据它们在列表中的顺序被检查
    3.如果对下一个类存在两个合法的选择,选择第一个父类
    

    二:如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:

    都是一个分支一个分支地找下去,然后最后找object

    class E:
        # def test(self):
        #     print('from E')
        pass
    
    
    class F:
        def test(self):
            print('from F')
    
    
    class B(E):
        # def test(self):
        #     print('from B')
        pass
    
    
    class C(F):
        # def test(self):
        #     print('from C')
        pass
    
    
    class D:
        def test(self):
            print('from D')
    
    
    class A(B, C, D):
        # def test(self):
        #     print('from A')
        pass
    
    
    # 新式类
    # print(A.mro()) # A->B->E->C->F->D->object
    
    obj = A()
    obj.test()  # 结果为:from F
    

    三:如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:

    经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)

    新式类:广度优先,会在检索最后一条分支的时候检索大脑袋

    class G:  # 在python2中,未继承object的类及其子类,都是经典类
        # def test(self):
        #     print('from G')
        pass
    
    
    class E(G):
        # def test(self):
        #     print('from E')
        pass
    
    
    class F(G):
        def test(self):
            print('from F')
    
    
    class B(E):
        # def test(self):
        #     print('from B')
        pass
    
    
    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
    
    
    # 新式类
    # print(A.mro()) # A->B->E->C->F->D->G->object
    
    # 经典类:A->B->E->G->C->F->D
    obj = A()
    obj.test()  #
    

    四:总结:

    多继承到底要不用???

    要用,但是规避几点问题

    1.继承结构尽量不要过于复杂

    2.推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系

  • 相关阅读:
    Echarts实现区级行政区划地图
    webpack4.x开发环境配置
    说说说vue.js中的组
    原生JS实现购物车功能详解
    面向对象的一些重要的基本概念
    Lucene教程
    开启事务时mybatis返回主键id
    生成二维码的方法,基于zxing
    mysql判断一条记录是否存在,如果存在,则更新此语句,如果不存在,则插入
    一:验证微信的Token
  • 原文地址:https://www.cnblogs.com/xuexianqi/p/12669925.html
Copyright © 2020-2023  润新知