Python 多重继承

本文最后更新于:2022年7月4日 上午

继承是面向对象编程的一个重要方式,可以扩展父类的功能,而Python作为热门的编程语言,同样具备该功能;除此之外,Python还有多重继承,本文记录 Python 多重继承相关内容。

简介

  • Python 继承机制使得子类可以获取父类的功能,在基础上增加、重写以实现新的功能,代码复用率高,易维护。
  • Python 支持多重继承,也就是为一个类可以指定多个父类
  • 在多重继承中,所有基类的特征都被继承到派生类中。多重继承的语法类似于单继承

语法

  • 在Python 建立类时,类名的 () 括号中添加多个类,即可实现多重继承
1
2
3
4
5
6
7
8
class Base1:
pass

class Base2:
pass

class MultiDerived(Base1, Base2):
pass

继承顺序

  • 多重继承,会使子类同时拥有多个父类,并获得所有父类中的方法,如果所有父类的方法(包括父类的所有父类)均不重名,那么一切都很和谐,多重继承后的子类将无争议地继承所有祖辈的财富
  • 但当父类们及其祖辈们的方法之间有重名内容则涉及到继承顺序的问题

多级继承

  • 发散一下类比多级继承,多级继承即祖先派生子类,该子类再派生子类,以此类推
classDiagram
class A
A: func test()
class B
B: func test()
class C{
func test()
}
A <|-- B
B <|-- C
  • 此种继承方式虽然也会有属性重名的问题,但均为当前类与父类之间的重名冲突,而且解决冲突的方式也是贪心的 —— 子类方法永远会覆盖父类方法,因此在使用时没有歧义,不需要注意什么顺序的问题

多重继承

  • 多重继承相对复杂,需要对 Python 的继承顺序有所了解才会得到我们需要的子类

考虑一个丧心病狂的多重继承场景,继承顺序由左到右

classDiagram
class A{
func test()
}
class B{
func test()
}
class C{
func test()
}
class D{
func test()
}
class E{
func test()
}
class F{
func test()
}
class G{
func test()
}
class H{
func test()
}
class I{
func test()
}
class J{
func test()
}
class K{
func test()
}
class L{
func test()
}
class M{
func test()
}
A <|-- M
C <|-- M
B <|-- C
D <|--B
K <|-- M
J <|-- K
I <|-- K
H <|-- J

G <|-- J
G <|-- I
L <|-- I
E <|-- H
F <|-- G
D <|-- E
D <|-- F

  • 对应 Python 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class A:
def test(self):
print('class A called.')
pass
class D:
def test(self):
print('class D called.')
pass
class B(D):
def test(self):
print('class B called.')
pass
class C(B):
def test(self):
print('class C called.')
pass
class E(D):
def test(self):
print('class E called.')
pass
class F(D):
def test(self):
print('class F called.')
pass
class G(F):
def test(self):
print('class G called.')
pass
class H(E):
def test(self):
print('class H called.')
pass
class L:
def test(self):
print('class L called.')
pass
class I(G, L):
def test(self):
print('class I called.')
pass
class J(H, G):
def test(self):
print('class J called.')
pass
class K(J, I):
def test(self):
print('class K called.')
pass
class M(A, C, K):
def test(self):
print('class M called.')
pass
if __name__ == '__main__':
obj = M()
obj.test()
  • 为了探究这种复杂情况的继承顺序问题,构造了树形继承冲突,将当前打印的类方法挨个注释掉,得到最终的继承顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
class M called.
class A called.
class C called.
class B called.
class K called.
class J called.
class H called.
class E called.
class I called.
class G called.
class F called.
class D called.
class L called.
  • 也就是说继承顺序为(序号小的优先级高):
classDiagram
class A{
2
}
class B{
4
}
class C{
3
}
class D{
12
}
class E{
8
}
class F{
11
}
class G{
10
}
class H{
7
}
class I{
9
}
class J{
6
}
class K{
5
}
class L{
13
}
class M{
1
}
A <|-- M
C <|-- M
B <|-- C
D <|-- B : ×
K <|-- M
J <|-- K
I <|-- K
H <|-- J

G <|-- J : ×
G <|-- I
L <|-- I
E <|-- H
F <|-- G
D <|-- E : ×
D <|-- F

  • 总结规律:

    • 继承顺序基本上遵循深度优先搜索
    • 遇到多重继承了一个多重继承的类时,按照被继承的多重继承顺序继承(由左到右)
    • 当多个父类继承同一个类时,优先继承子类,辈分越大越晚被继承,相当于仅有最后一个类继承了祖先类
  • 这是我们根据实验现象总结的规律,看下官方对多重继承顺序的描述:

    • 官方文档:https://docs.python.org/3/tutorial/classes.html?highlight=multiple inheritance#multiple-inheritance

      For most purposes, in the simplest cases, you can think of the search for attributes inherited from a parent class as depth-first, left-to-right, not searching twice in the same class where there is an overlap in the hierarchy. Thus, if an attribute is not found in DerivedClassName, it is searched for in Base1, then (recursively) in the base classes of Base1, and if it was not found there, it was searched for in Base2, and so on.

    • 和我们观察到的现象是一致的

  • 总结要点

    • 深度优先
    • 由左到右
    • 同一个节仅搜索一次

参考资料


Python 多重继承
https://www.zywvvd.com/notes/coding/python/python-multi-inheritance/python-multi-inheritance/
作者
Yiwei Zhang
发布于
2022年5月26日
许可协议