你提到的 Python 类继承链中 super() 的非直觉行为,确实是很多人初学或即使用了一段时间也容易踩坑的地方。我们可以慢慢拆开来看,并通过示例理解它背后的逻辑。
1. super() 并不总是指父类
很多人直觉认为:
class A:
def f(self):
print("A")
class B(A):
def f(self):
print("B")
super().f() # “调用父类 A 的 f”
在这个简单单继承链里,确实是这样,super() 调用的是父类 A 的 f() 方法。
但是在 多重继承 或 菱形继承(diamond inheritance)里,super() 的行为就很容易出乎意料。
2. super() 实际上调用的是 MRO(Method Resolution Order)链上的下一个方法
Python 的类有一个 MRO 顺序,这个顺序决定了 super() 调用的“下一个类”是谁,而不是单纯的父类。
class A:
def f(self):
print("A")
class B(A):
def f(self):
print("B")
super().f()
class C(A):
def f(self):
print("C")
super().f()
class D(B, C):
def f(self):
print("D")
super().f()
d = D()
d.f()
输出是:
D
B
C
A
为什么不是你直觉中的 “D → B → A → C” 或其他顺序?
D 的 MRO 是:D → B → C → A → object
可以用 D.__mro__ 或 help(D) 看。
每个 super().f() 实际上调用 MRO 中当前类之后的下一个类的方法,而不是直接调用父类。
所以 B 里的 super().f() 调用的是 MRO 里 B 之后的类 C 的 f 方法,而不是 A。
3. 非直觉场景:多次调用 super() 与构造函数
很多人以为:
class A:
def __init__(self):
print("A init")
class B(A):
def __init__(self):
print("B init")
super().__init__()
class C(A):
def __init__(self):
print("C init")
super().__init__()
class D(B, C):
def __init__(self):
print("D init")
super().__init__()
D()
输出:
D init
B init
C init
A init
❌ 注意这里:
每个类都调用了 super().__init__()。
C 的 super().__init__() 不是调用 A 的父类,而是 MRO 中 C 之后的类 A。
A 是最终结束点,object.__init__ 不做任何事。
这在多重继承时非常重要:你可以安全地在每个类里调用 super() 来确保整个继承链都初始化,而不需要知道具体父类是谁。这也是 Python 的 协作式多重继承(cooperative multiple inheritance) 的核心。
4. 一些容易混淆的坑
直接使用父类而非 super()
class B(A):
def f(self):
print("B")
A.f(self) # 直接调用父类
如果在多重继承中,这会破坏 MRO,可能导致某些类的方法被跳过。
super() 不一定指“上一层父类”
它指 MRO 中当前类之后的下一个类。
在单继承里通常符合直觉,在多继承里必须看 MRO。
使用 super() 时要传 self
Python 3 里,super() 省略参数默认是 super(CurrentClass, self)。
在 Python 2 中,需要写 super(CurrentClass, self).method()。
5. 小结
super() 的目标是调用 MRO 链上的下一个方法,不是简单的“父类方法”。
在多重继承中,super() 让你写的类可以 安全协作,每个类都可以调用 super() 而不关心实际继承链细节。
“非直觉行为”主要出现在多继承和菱形继承模式下,或者当你直接想象 super() 就是父类时。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70050070/viewspace-3171526/ ,如需转载,请注明出处,否则将追究法律责任。