码迷,mamicode.com
首页 > 其他好文 > 详细

多继承带来的菱形问题

时间:2020-04-10 00:14:57      阅读:75      评论:0      收藏:0      [点我收藏+]

标签:关系   erp   height   text   复杂   就是   class   vertica   面向对象语言   

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

技术图片

这种继承结构下导致的问题称之为菱形问题:如果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这一条分支的顺序直到找到我们想要的属性

技术图片

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,分别对应属性的两种查找方式:深度优先和广度优先技术图片

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

技术图片

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机制:在多继承的背景下满足继承的什么"是"什么的关系

多继承带来的菱形问题

标签:关系   erp   height   text   复杂   就是   class   vertica   面向对象语言   

原文地址:https://www.cnblogs.com/bailongcaptain/p/12670533.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!