You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python多继承执行逻辑疑问:代码输出与预期不符的原因解析

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()的调用链是这样的:

  1. 找到A.test()并执行
  2. A.test()里的super().test():这里的super()不是直接找A的父类Base,而是按C的MRO顺序,A的下一个类是B,所以会调用B.test()
  3. B.test()里的super().test():按MRO,B的下一个是Base,所以调用Base.test(),打印I am B(此时self.name已经被B.__init__设为B
  4. 执行完Base.test()后,回到B.test(),打印Called from B
  5. 执行完B.test()后,回到A.test(),打印Called from A

这就是为什么I am B只出现一次,而两个Called from各出现一次——整个调用是一条完整的链,不是分别触发ABtest()两次。


二、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类属性,不是实例属性——AB__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顺序,最终取Ainternal_name


补充:如果想分别触发ABtest()逻辑

如果你的预期是分别调用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()的行为;同时要区分类属性和实例属性的不同查找规则,这样就能理清多继承中的各种奇怪现象啦~

火山引擎 最新活动