python | 关于多重继承那些事

简介: 继承是面向对象编程的一个重要的方式 ,通过继承 ,子类就可以扩展父类的功能 。和 c++ 一样 ,在 python 中一个类能继承自不止一个父类 ,这叫做 python 的多重继承(Multiple Inheritance )。多重继承的语法与单继承类似 。

什么是多重继承

继承是面向对象编程的一个重要的方式 ,通过继承 ,子类就可以扩展父类的功能 。和 c++ 一样 ,在 python 中一个类能继承自不止一个父类 ,这叫做 python 的多重继承(Multiple Inheritance )。多重继承的语法与单继承类似 。

class SubclassName(BaseClass1, BaseClass2, BaseClass3, ...):
    pass

当然 ,子类所继承的所有父类同样也能有自己的父类 ,这样就可以得到一个继承关系机构图如下图所示 :

4.jpg


在 python 中 ,钻石继承首先体现在父类方法的调用顺序上 ,比如若B和C同时重写了 A 中的某个方法时 :

class A(object):
    def m(self):
        print("m of A called")
class B(A):
    def m(self):
        print("m of B called")
class C(A):
    def m(self):
        print("m of C called")
class D(B,C):
    pass

如果我们实例化 D 为 d ,然后调用 d.m() 时 ,会输出 "m of B called",如果 B 没有重写 A 类中的 m 方法时 :

class A(object):
    def m(self):
        print("m of A called")
class B(A):
    pass
class C(A):
    def m(self):
        print("m of C called")
class D(B,C):
    pass

此时调用 d.m 时,则会输出 "m of C called" , 那么如何确定父类方法的调用顺序呢 ,这一切的根源还要讲到方法解析顺序(Method Resolution Order,MRO),这一点我们等会再将。

钻石继承还有一个问题是 ,比如若 B 和 C 中的 m 方法也同时调用了 A 中的m方法时 :

class A:
    def m(self):
        print("m of A called")
class B(A):
    def m(self):
        print("m of B called")
        A.m(self)
class C(A):
    def m(self):
        print("m of C called")
        A.m(self)
class D(B,C):
    def m(self):
        print("m of D called")
        B.m(self)
        C.m(self)

此时我们调用 d.m ,A.m 则会执行两次。

m of D called
m of B called
m of A called
m of C called
m of A called

这种问题最常见于当我们初始化 D 类时 ,那么如何才能避免钻石继承问题呢 ?

super and MRO

其实上面两个问题的根源都跟 MRO 有关 ,MRO(Method Resolution Order) 也叫方法解析顺序 ,主要用于在多重继承时判断调的属性来自于哪个类 ,其使用了一种叫做 C3 的算法 ,其基本思想时在避免同一类被调用多次的前提下 ,使用广度优先和从左到右的原则去寻找需要的属性和方法 。当然感兴趣的同学可以移步 :MRO介绍 。

比如针对如下的代码 :

>>> class F(object): pass
>>> class E(object): pass
>>> class D(object): pass
>>> class C(D,F): pass
>>> class B(D,E): pass
>>> class A(B,C): pass

当你打印 A.__mro__ 时可以看到输出结果为 :

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

如果我们实例化 A 为 a 并调用 a.m() 时 ,如果 A 中没有 m 方法 ,此时python 会沿着 MRO 表逐渐查找 ,直到在某个父类中找到m方法并执行 。

那么如何避免顶层父类中的某个方法被执行多次呢 ,此时就需要super()来发挥作用了 ,super 本质上是一个类 ,内部记录着 MRO 信息 ,由于 C3 算法确保同一个类只会被搜寻一次 ,这样就避免了顶层父类中的方法被多次执行了 ,比如针对钻石继承问题 2 中的代码可以改为 :

class A(object):
    def m(self):
        print("m of A called")
class B(A):
    def m(self):
        print("m of B called")
        super().m()
class C(A):
    def m(self):
        print("m of C called")
        super().m()
class D(B,C):
    def m(self):
        print("m of D called")
        super().m()

此时打印的结果就变成了 :

m of D called
m of B called
m of C called
m of A called

结论

多重继承问题是个坑 ,很多编程语言中并没有多重继承的概念 ,毕竟它带来的麻烦比能解决的问题都要多 ,所以如果不是特别必要 ,还是尽量少用多重继承 。如果你非要用 ,那你要好好研究下类的层次结构 ,至少要对 C3 算法具有一定的了解吧 ,比如弄懂下面的代码哪里出了问题 ?

>>> F=type('Food',(),{'remember2buy':'spam'})
>>> E=type('Eggs',(F,),{'remember2buy':'eggs'})
>>> G=type('GoodFood',(F,E),{}) 
# TypeError: Cannot create a consistent method resolution
# order (MRO) for bases Food, Eggs


相关文章
|
4月前
|
算法 Python
Python 面向对象编程:什么是多重继承,Python 中如何处理多重继承?
Python 面向对象编程:什么是多重继承,Python 中如何处理多重继承?
|
8月前
|
算法 Python
如何实现Python中的多重继承(Multiple Inheritance)以及方法解析顺序(MRO)
如何实现Python中的多重继承(Multiple Inheritance)以及方法解析顺序(MRO)
210 0
|
Python
Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)2
Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)2
89 0
|
测试技术 Python
Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)
Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)
225 0
|
Python
Python多重继承
Python多重继承自制脑图 一个类中可以有多重父类:在 Python 中是支持多重继承的,也就是我们可以为一个类同时指定多个父类 可以在类名的( )后边添加多个类,来实现多重继承 在开发中没有特殊的情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂. 如果多个父类中有同名的方法,则会现在第一个父类中寻找,然后找第二个,然后找第三个。 多重继承的特点:前边父类的方法会覆盖后边父类的方法。
87 0
Python多重继承
|
Python
Python的封装继承多态和多重继承 | Python 主题月
Python的封装继承多态和多重继承 | Python 主题月
142 0
多重继承 | Python从入门到精通:高阶篇之三十二
在Python中是支持多重继承的,也就是我们可以为一个类同时指定多个父类。这是与别的大部分语言不一致的。但在开发中没有特殊的情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂。
多重继承 | Python从入门到精通:高阶篇之三十二
|
Python 程序员 算法
python 继承与多重继承
当然,如果不支持python继承,语言特性就不值得称为“类”。派生类定义的语法如下所示: <statement-1> . . . <statement-N> 名称 BaseClassName 必须定义于包含派生类定义的作用域中。

热门文章

最新文章