Python多继承执行逻辑疑问:代码输出与预期不符的原因解析
先把你的代码和输出复现出来,方便对照分析:
class Base: name = "Base" def test(self): print("I am", self.name) class A(Base): internal_name = "A" def __init__(self): self.name = "A" super().__init__() def test(self): super().test() print("Called from A") class B(Base): internal_name = "B" def __init__(self): self.name = "B" super().__init__() def test(self): super().test() print("Called from B") class C(A, B): def __init__(self): self.name = "C" A.__init__(self) B.__init__(self) def identify(self): print("Internal name:", self.internal_name) c = C() c.test() c.identify()
实际输出:
I am B
Called from B
Called from A
Internal name: A
下面分点拆解你所有的疑问:
一、为什么test()的输出是一条链?MRO方法解析顺序在起作用
你困惑的核心是Python多继承的MRO(Method Resolution Order)规则,这是多继承中方法调用的核心逻辑。
当你调用c.test()时,因为C没有定义自己的test(),Python会按照C的MRO顺序查找方法。你可以通过print(C.__mro__)看到C的MRO是:C → A → B → Base。
整个test()的调用链是这样的:
- 找到
A.test()并执行 A.test()里的super().test():这里的super()不是直接找A的父类Base,而是按C的MRO顺序,A的下一个类是B,所以会调用B.test()B.test()里的super().test():按MRO,B的下一个是Base,所以调用Base.test(),打印I am B(此时self.name已经被B.__init__设为B)- 执行完
Base.test()后,回到B.test(),打印Called from B - 执行完
B.test()后,回到A.test(),打印Called from A
这就是为什么I am B只出现一次,而两个Called from各出现一次——整个调用是一条完整的链,不是分别触发A和B的test()两次。
二、self.name最终为B:你的理解是对的
这部分你的预期是正确的:
C.__init__先把self.name设为"C"- 调用
A.__init__(self):将self.name改为"A",然后调用Base.__init__(无操作) - 调用
B.__init__(self):将self.name改为"B",然后调用Base.__init__(无操作)
所以最终self.name是"B",这和Base.test()的打印结果一致。
三、internal_name为"A":类属性的继承规则
internal_name是类属性,不是实例属性——A和B的__init__里都没有给self.internal_name赋值,所以当你调用self.internal_name时,Python会从类的继承链中查找。
根据C的MRO顺序(C→A→B→Base),Python会先在C的类属性中找internal_name,找不到就去A里找,找到A.internal_name = "A"后就停止查找,不会再去B里找。
哪怕你最后调用了B.__init__,但B的__init__没有修改实例的internal_name,所以类属性的查找依然遵循MRO顺序,最终取A的internal_name。
补充:如果想分别触发A和B的test()逻辑
如果你的预期是分别调用A.test()和B.test(),需要在C中显式定义test():
class C(A, B): # ... 其他代码不变 def test(self): A.test(self) B.test(self)
这样调用c.test()会输出:
I am B Called from B Called from A I am B Called from B Called from A
但这种写法违背了多继承的设计初衷,推荐用super()遵循MRO顺序来避免逻辑混乱。
总结一下:多继承的核心是理解MRO顺序,它决定了方法查找和super()的行为;同时要区分类属性和实例属性的不同查找规则,这样就能理清多继承中的各种奇怪现象啦~




