python继承中super() 不是简单的“调用父类”,而是调用 MRO 里的下一个类
Python 里的一个类可以同时继承多个父类。这让我们的模型设计变得更灵
活,但同时也带来一个新问题:“在复杂的继承关系下,如何确认子类的
某个方法会用到哪个父类?”
这里有点需要理解:
- MRO(方法解析顺序) —— 当一个类有多个父类时,Python 是按照某种规则(MRO)来决定调用哪个父类的方法的。
- super() 的行为 ——
super()
并不是简单调用“父类”的方法,而是按照 MRO 的顺序找到“下一个”类的方法。
一、MRO(方法解析顺序)
在 Python 里,一个类可以继承多个父类(多重继承)。但这样会带来一个问题:
如果多个父类里有同名的方法,Python 应该优先调用哪个?
Python 通过 MRO(方法解析顺序) 解决这个问题。MRO 确定了方法的查找顺序,也就是当你调用 obj.method()
时,它会按照 MRO 里的顺序 找到第一个定义了 method()
的类。
🔹 示例 1:理解 MRO 的查找顺序
class A:
def say(self):
print("I'm A")
class B(A):
pass
class C(A):
def say(self):
print("I'm C")
class D(B, C): # D 同时继承 B 和 C
pass
print(D.mro()) # 查看 MRO 顺序
D().say() # 调用 say() 方法
输出:
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
I'm C
解释:
- MRO 计算出的查找顺序是 D → B → C → A → object。
D
里没有say()
方法,就去找B
。B
也没有say()
,继续往下找C
。C
里有say()
,所以调用的是C.say()
,而A.say()
被跳过了。
📌 关键点:MRO 采用 C3 线性化算法,主要遵循**“从左到右优先”**,也就是 D(B, C)
里 B 比 C 优先,但如果 B
里没有 say()
,就继续往后找 C
。
二、super() 的行为
很多人以为 super()
只是调用“父类”的方法,但其实它是按 MRO 顺序寻找“下一个类”,而不一定是“直接的父类”!
🔹 示例 2:super() 的 MRO 查找顺序
class A:
def __init__(self):
print("I'm A")
super().__init__()
class B(A):
def __init__(self):
print("I'm B")
super().__init__()
class C(A):
def __init__(self):
print("I'm C")
super().__init__()
class D(B, C): # D 继承 B 和 C
pass
D() # 实例化 D
输出:
I'm B
I'm C
I'm A
解释:
D
里没有__init__()
,所以会调用B.__init__()
(因为 MRO 里B
在C
前面)。B.__init__()
里调用super().__init__()
,MRO 里B
后面是C
,所以super()
进入C.__init__()
。C.__init__()
里也有super().__init__()
,MRO 里C
后面是A
,所以最终调用A.__init__()
。
📌 关键点:super()
调用的不是“父类”,而是 MRO 里的下一个类,所以 B
里 super()
其实是调用 C.__init__()
,而不是 A.__init__()
。
三、为什么多重继承容易出问题?
多重继承虽然强大,但如果不小心设计,很容易导致 MRO 复杂,代码难以维护。
🔹 示例 3:设计不好的多重继承
class Animal:
def move(self):
print("Animal is moving")
class Flyable:
def move(self):
print("Flying in the sky")
class Bird(Animal, Flyable):
pass
b = Bird()
b.move() # 你觉得会输出什么?
输出:
Animal is moving
问题:
Bird
继承了Animal
和Flyable
,但Animal
的move()
覆盖了Flyable
的move()
,所以Bird().move()
只会执行Animal.move()
。- 这个设计就会让
Bird
不能“飞”,不符合直觉。
📌 如何解决?
可以用 super()
让 move()
遵循 MRO 顺序:
class Animal:
def move(self):
print("Animal is moving")
super().move() # 继续往 MRO 里的下一个类找
class Flyable:
def move(self):
print("Flying in the sky")
class Bird(Animal, Flyable):
pass
b = Bird()
b.move()
输出:
Animal is moving
Flying in the sky
解决方式:
Animal.move()
先执行完自己的逻辑,再super().move()
,调用 MRO 里的下一个Flyable.move()
。
四、总结
- MRO(方法解析顺序) 决定了多重继承下方法的调用顺序,MRO 通过 C3 线性化算法 计算出来。
super()
不是简单的“调用父类”,而是调用 MRO 里的下一个类,这意味着它会受到 MRO 的影响。- 多重继承可能导致意外的行为,尤其是在多个父类里有相同的方法时。
- 在使用
super()
时,建议所有父类的方法都调用super()
,避免意外跳过某些方法。 - 如果发现自己用多重继承写了很复杂的 MRO 逻辑,应该考虑是否能用更简单的方式(如组合)来解决问题。
五、建议
❌ 不要 过度使用多重继承,除非你真的需要它。
✅ 可以 考虑用 接口(Protocol)、组合(Composition) 来代替复杂的继承结构。
✅ 如果必须用多重继承,一定要理解 MRO 和 super()
的行为,否则容易踩坑。