Python进阶 —— 面向对象编程 —— 钻石继承

作者是一名沉迷于Python无法自拔的蛇友,为提高水平,把Python的重点和有趣的实例发在上。

面向对象编程

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP 达到了软件工程的三个主要目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其它对象发送信息。一项由 Deborah J. Armstrong 进行的长达40年之久的计算机著作调查显示出了一系列面向对象程序设计的基本理论。
(来源于不说人话的百度百科)

个人理解:在Python中,面向对象编程使用class关键字,就好似构建一个整体的框架,使一切都变得更方便。

多继承

注意:下面的图片只适用于Python2,Python3已全部是新式类!!!

Python进阶 —— 面向对象编程 —— 钻石继承_第1张图片
image.png

钻石继承(继承于多继承)

为什么叫做钻石继承,说白了就是给一个见鬼的缺陷起一个好听的名字(大部分主流语言都禁止多继承)
还有,看看图片就明白了。


Python进阶 —— 面向对象编程 —— 钻石继承_第2张图片
钻石继承

请看一下示例:

class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")

class B(A):
    def __init__(self):
        print("进入B…")
        A.__init__(self)
        print("离开B…")
        
class C(A):
    def __init__(self):
        print("进入C…")
        A.__init__(self)
        print("离开C…")

class D(B, C):
    def __init__(self):
        print("进入D…")
        B.__init__(self)
        C.__init__(self)
        print("离开D…")

>>> d = D()
进入D…
进入B…
进入A…
离开A…
离开B…
进入C…
进入A…
离开A…
离开C…
离开D…

钻石继承(菱形继承)会带来什么问题?

多重继承容易导致钻石继承(菱形继承)问题,上边代码实例化 D 类后我们发现 A 被前后进入了两次

有童鞋说两次就两次憋,我女朋友还不止呢......

这有什么危害?

我举个例子,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。

如何避免钻石继承(菱形继承)问题?

为解决这个问题,Python 使用了一个叫“方法解析顺序(Method Resolution Order,MRO)”的东西,还用了一个叫 C3 的算法。

该算法相对来说比较复杂(有兴趣深入算法的朋友可以阅读:https://www.python.org/download/releases/2.3/mro)

当然我这里愿意跟你解释下 MRO 的顺序基本就是:在避免同一类被调用多次的前提下,使用广度优先和从左到右的原则去寻找需要的属性和方法。

在继承体系中,C3 算法确保同一个类只会被搜寻一次。例子中,如果一个属性或方法在 D 类中没有被找到,Python 就会搜寻 B 类,然后搜索 C类,如果都没有找到,会继续搜索 B 的基类 A,如果还是没有找到,则抛出“AttributeError”异常。

你可以使用 类名.mro 获得 MRO 的顺序(注:object 是所有类的基类,金字塔的顶端):

 >>> D.__mro__
(, , , , )

然而,Python解决了这个问题,super()让你不再苦恼。

class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")

class B(A):
    def __init__(self):
        print("进入B…")
        super().__init__()
        print("离开B…")
        
class C(A):
    def __init__(self):
        print("进入C…")
        super().__init__()
        print("离开C…")

class D(B, C):
    def __init__(self):
        print("进入D…")
        super().__init__()
        print("离开D…")

>>> d = D()
进入D…
进入B…
进入C…
进入A…
离开A…
离开C…
离开B…
离开D…

这样就没问题了。

总结

虽然Python解决了这个问题,但是笔者建议大家尽可能避免多继承问题,它会使你的程序出现一些始料不及的情况。

你可能感兴趣的:(Python进阶 —— 面向对象编程 —— 钻石继承)