问题: 什么是菱形继承?

👇 点击展开/折叠 面试背诵模板(建议先背这个) 菱形继承指一个子类同时继承自两个父类,而这两个父类又有共同的祖先类,形成的继承结构图呈菱形。其核心问题是“方法解析顺序(MRO)冲突”和“重复继承”。Python 通过 C3 线性化算法(保证子类优先、父类从左到右、单调性)计算 MRO,并结合 super() 按 MRO 链调用,确保每个父类方法只被执行一次,从而有效规避了重复继承的问题。C3 线性化保证了每个基类在 MRO 中仅出现一次。
---

菱形继承的结构图示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A:
def method(self): print("A")

class B(A):
def method(self): print("B")

class C(A):
def method(self): print("C")

class D(B, C):
pass

d = D()
d.method() # 输出 "B"(遵循 MRO 顺序)
print(D.__mro__) # (D, B, C, A, object)

菱形继承的核心问题

问题类型 描述 Python 处理方式
调用冲突 当 B 和 C 都重写了 A 的方法,D 不知道选哪个 按 MRO 从左到右查找,选第一个匹配的
重复调用 若按传统继承,A 的方法可能被 B 和 C 各调用一次,导致重复执行 super() 遵循 MRO,每个类仅调用一次
初始化重复 __init__ 可能被多个父类重复执行 使用 super().__init__() 确保按 MRO 链调用一次

MRO 核心查找规则

Python 使用 C3 线性化算法,严格遵循三条原则:

  1. 子类优先于父类。
  2. 父类按声明顺序从左到右。
  3. 保持单调性(即 MRO 中不会出现某个类在另一个类之前,而另一个类在其继承链中要求它之后的情况)。
1
2
3
class D(B, C):
pass
# 查找顺序:D → B → C → A → object

super() 在菱形继承中的正确使用

super() 不仅能调用直接父类,而是沿着 MRO 链依次调用。在菱形结构中,super().method() 会按 C3 顺序逐个调用,且每个类的方法仅执行一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A:
def method(self):
print("A")

class B(A):
def method(self):
print("B")
super().method()

class C(A):
def method(self):
print("C")
super().method()

class D(B, C):
def method(self):
print("D")
super().method()

D().method()
# 输出顺序:D → B → C → A

最佳实践与避坑建议

  • 在设计多重继承时,优先使用协作式多重继承(所有父类都使用 super(),而非硬编码 父类名.方法)。
  • 对于 __init__,确保所有父类都使用 super().__init__(),且参数保持一致。
  • 尽量避免深度超过三层或结构过复杂的菱形继承,必要时考虑用组合替代(如将公共功能拆为独立类,通过属性引用而非继承)。
  • 检查 MRO 是否合理:print(cls.__mro__) 有助于调试方法调用顺序是否符合预期。