使用super().__init__报错但直接调用父类__init__正常的原因探究
问题:super()与直接调用父类__init__的差异分析
代码示例
class A: def __init__(self, a, b, c, d=0, e=0, f=0): self.a = a self.b = b self.c = c self.d = d self.e = e self.f = f class B(A): def __init__(self, a, b, c, d, **kwargs): super().__init__(a, b, d, **kwargs) # A.__init__(self, a, b, d, **kwargs) self.c = c class C(A): def __init__(self, a, b, c, d, **kwargs): super().__init__(a, b, d, **kwargs) # A.__init__(self, a, b, d, **kwargs) self.c = c class D(B, C): def __init__(self, a, b, c, d, e, **kwargs): super().__init__(a, b, c, e, **kwargs) # B.__init__(self, a, b, c, e, **kwargs) C.__init__(self, a, b, d, e) class E(D): def __init__(self, a, b, c, d, e=0, **kwargs): super().__init__(a, b, c, d, e, **kwargs) # D.__init__(self, a, b, c, d, e, **kwargs) if __name__ == '__main__': E(None, None, None, 0)
报错信息
Traceback (most recent call last): File "/Users/user/Library/Application Support/JetBrains/PyCharm2023.2/scratches/scratch_5.py", line 39, in <module> E(None, None, None, 0) File "/Users/user/Library/Application Support/JetBrains/PyCharm2023.2/scratches/scratch_5.py", line 34, in __init__ super().__init__(a, b, c, d, e, **kwargs) File "/Users/user/Library/Application Support/JetBrains/PyCharm2023.2/scratches/scratch_5.py", line 27, in __init__ super().__init__(a, b, c, e, **kwargs) File "/Users/user/Library/Application Support/JetBrains/PyCharm2023.2/scratches/scratch_5.py", line 13, in __init__ super().__init__(a, b, d, **kwargs) TypeError: C.__init__() missing 1 required positional argument: 'd'
用户疑问
替换所有super().__init__为直接调用父类__init__(如注释中的写法)后代码可正常运行,想知道:
- 两种调用方式结果不同的原因
super().__init__的工作机制- 简化逻辑
class A(B,C)中super().__init__()等同于B.__init__()是否错误
解答
核心结论:super()遵循MRO方法解析顺序,而非固定指向定义时的第一个父类
1. Python的MRO(方法解析顺序)
Python在处理多继承时,会为每个类生成一个线性的方法查找顺序(MRO),它决定了调用父类方法时的优先级。你的代码中,类E的MRO为:E → D → B → C → A → object
2. super()的真实工作逻辑
super()并不是简单指向当前类的直接父类,而是根据调用该方法的实例的MRO,找到当前类在MRO中的下一个类。
在报错场景中:
- 创建
E实例时,调用链为:E.__init__→D.__init__→B.__init__ - 当执行
B.__init__里的super().__init__(a, b, d, **kwargs)时,由于当前实例的MRO是E→D→B→C→A→object,super()指向的是MRO中B的下一个类——C,而非B的直接父类A - 但
C.__init__要求传入a,b,c,d四个必选参数,B只传了a,b,d,缺少c参数,因此触发TypeError
3. 直接调用父类__init__的差异
当你将super().__init__替换为A.__init__(self, ...)时,是直接指定调用A的构造方法,完全绕过了MRO的自动查找逻辑,不会触发C.__init__,因此不会出现参数不匹配的问题。
4. 你的简化逻辑为什么错误
对于代码:
class A(B, C): def __init__(self): super().__init__() # 这不等同于B.__init__()吗?
这个逻辑仅在单继承场景下成立。在多继承中:
- 如果是
A自身的实例调用__init__,super()确实指向B - 但如果
A被其他多继承类(比如D(A, X))继承,当D的实例调用A.__init__时,super()指向的是X而非B
简言之,super()的指向始终由调用它的实例的MRO决定,而非定义它的类的父类顺序。
内容的提问来源于stack exchange,提问作者a82fd72deb
相关产品推荐
相关产品推荐

