多继承带来的菱形问题

大多数面向对象语言都不支持多继承,而在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机制:在多继承的背景下满足继承的什么"是"什么的关系

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

时间: 2024-11-05 21:36:55

多继承带来的菱形问题的相关文章

property装饰器_继承

property装饰器 一:装饰器 装饰器是在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加 # 新功能的可调用对象 # print(property) property是一个装饰器,是用来绑定给对象的方法伪造成一个数据属性 二:案例 """ 成人的BMI数值: 过轻:低于18.5 正常:18.5-23.9 过重:24-27 肥胖:28-32 非常肥胖, 高于32 体质指数(BMI)=体重(kg)÷身高^2(m) EX:70kg÷(1.75×1.75)=22.86

C++之菱形继承

当我们谈C++时,我们谈些什么? 封装,继承,多态.这是C++语言的三大特性,而每次在谈到继承时我们不可避免的要谈到一个很重要的问题--菱形继承. a.菱形继承是什么 如上图,菱形继承即多个类继承了同一个公共基类,而这些派生类又同时被一个类继承.这么做会引发什么问题呢,让我们来看一段代码吧! #include<iostream> using namespace std; class Base { protected: int _base; public: void fun() { cout &

菱形继承

学习了C++的继承,觉得菱形继承是比较难懂的一部分,通过了解菱形继承,我们可以了解编译器是如何工作的,下面介绍一下菱形继承在内存中的实现方式. 首先简单了解一下虚继承,下面父类和子类的大小分别是多少? class Base { public:              virtual void fun1()             {                         cout << "Base::fun1()" << endl;         

【C++】虚拟菱形继承的解析

在继承中经常会遇到这种情况:有一个超类A,子类B1,B2继承了A,而类D又继承了父类B1,B2.在这种情况下如果按照我们以前的正常的菱形继承的话会有一个问题是子类C会继承两次超类A中的成员,当在C中想访问继承来自B1,B2中A的元素会出现两个问题: 问题一.数据的冗余 问题二.访问的二意性 出现了这种问题那么我们该如何解决呢? C++中为了解决这个问题引入了虚拟菱形继承,那么虚拟菱形继承是怎么解决的呢? 首先给大家先画两个图比较下菱形继承和虚拟菱形继承在继承时在内存中的成员分布情况: 一. 菱形

菱形继承问题

#coding:utf-8 # python2中指定文件头'''1.菱形继承 当一个子继承多个父类时,多个父类最终继承了同一个类,称之为菱形继承 2.菱形继承的问题: python2区分经典类与新式类,如果子的继承是一个菱形继承,那么经典类与形式的区别为? 经典类下查找属性:深度优先查找 深度查找:比如A(B,C,D),深度查找就是A沿着B一路查找下去,直到世界尽头(最后一个父类), 然后才会跳到C查找,找到最后再跳到C 新式类下查找属性:广度优先查找 A(B,C,D)沿着A-B 不插着最后一个

C++反汇编第四讲,认识多重继承,菱形继承的内存结构,以及反汇编中的表现形式.

目录: 1.多重继承在内存中的表现形式 多重继承在汇编中的表现形式 2.菱形继承 普通的菱形继承 虚继承 汇编中的表现形式 一丶多重继承在内存中的表现形式 高级代码: class Father1 { public: Father1(){}//空构造 virtual ~Father1(){} //空析构 virtual void Player(){} //玩耍的函数 int m_price;//金钱 }; class Father2 { public: Father2(){} virtual ~F

虚继承

------------------siwuxie095 看如下实例: 有 4 个类,其中:类 A 是父类,类 B 和 类 C 都继承 类 A, 而 类 D 继承了 类 B 和 类 C,称这种继承关系为 菱形继承 在菱形继承中,既有多继承,又有多重继承: 那么问题来了: 当实例化 D 的对象时,发现:D 是从 B 继承来的,B 是从 A 继承来的, D 也是从 C 继承来的,C 是从 A 继承来的 这样,D 中将含有两个完全一样的 A 的数据,这种情况是不能容忍的, 因为在一个对象中有两份完全相

C++里的继承和多态(上)

  继承 1.私有继承:基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问. 公有继承:基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的访问权限,而基类的私有成员在派生类中是不可见的. 在公有继承时,派生类的成员函数可以访问基类中的公有成员和保护成员:派生类的对象仅可以访问基类中的公有成员. 保护继承:基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数访问,不能被它派生类的对象访问. 2.注意: 1>基类的private成员

浅析C++继承与派生

测试环境: Target: x86_64-linux-gnu gcc version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2.1) 定义 要分析继承,首先当然要知道什么是继承:继承是面向对象程旭设计中使代码可以复用的最重要的手段,它允许程序员在原有类特性的基础上进行扩展,增加功能.这样产生的新类,就叫做派生类(子类).继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程. 继承的格式 class 子类名 :继承权限 基类名 比如下面分别定义了两