当前位置: 首页 > news >正文

python继承中super() 不是简单的“调用父类”,而是调用 MRO 里的下一个类

Python 里的一个类可以同时继承多个父类。这让我们的模型设计变得更灵
活,但同时也带来一个新问题:“在复杂的继承关系下,如何确认子类的
某个方法会用到哪个父类?”

这里有点需要理解:

  1. MRO(方法解析顺序) —— 当一个类有多个父类时,Python 是按照某种规则(MRO)来决定调用哪个父类的方法的。
  2. 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 里 BC 前面)。
  • B.__init__() 里调用 super().__init__(),MRO 里 B 后面是 C,所以 super() 进入 C.__init__()
  • C.__init__() 里也有 super().__init__(),MRO 里 C 后面是 A,所以最终调用 A.__init__()

📌 关键点super() 调用的不是“父类”,而是 MRO 里的下一个类,所以 Bsuper() 其实是调用 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 继承了 AnimalFlyable,但 Animalmove() 覆盖了 Flyablemove(),所以 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()

四、总结

  1. MRO(方法解析顺序) 决定了多重继承下方法的调用顺序,MRO 通过 C3 线性化算法 计算出来。
  2. super() 不是简单的“调用父类”,而是调用 MRO 里的下一个类,这意味着它会受到 MRO 的影响。
  3. 多重继承可能导致意外的行为,尤其是在多个父类里有相同的方法时。
  4. 在使用 super() 时,建议所有父类的方法都调用 super(),避免意外跳过某些方法。
  5. 如果发现自己用多重继承写了很复杂的 MRO 逻辑,应该考虑是否能用更简单的方式(如组合)来解决问题

五、建议

不要 过度使用多重继承,除非你真的需要它。
可以 考虑用 接口(Protocol)、组合(Composition) 来代替复杂的继承结构。
如果必须用多重继承,一定要理解 MRO 和 super() 的行为,否则容易踩坑。

相关文章:

  • LeetCode每日精进:225.用队列实现栈
  • 「pandas」Pandas 基本数据操作、 索引、赋值、排序
  • 网络工程师 (45)网际控制报文协议ICMP
  • blackbox.ai 一站式AI代理 畅享顶级模型
  • 如何使用 vxe-table grid 全配置式给单元格字段格式化内容,格式化下拉选项内容
  • MybatisPlus-扩展功能
  • Axure RP11 新功能:为设计师插上“翅膀”
  • Low code web framework for real world applications, in Python and Javascript
  • 基于SpringBoot+Vue的老年人体检管理系统的设计与实现(源码+SQL脚本+LW+部署讲解等)
  • Android JNI的理解与使用。
  • 获取某厂招聘岗位信息
  • linux 面试题
  • 后台管理系统-项目初始化
  • 网络编程(24)——实现带参数的http-get请求
  • Linux 文件内容查看
  • 力扣LeetCode: 740 删除并获得点数
  • 机器视觉--图像的运算(乘法)
  • EXCEL解决IF函数“您已为此函数输入太多个参数”的报错
  • 12. Docker 网络(bridge,host,none,container,自定义网络)配置操作详解
  • 通读【基于深度学习的网络异常流量检测研究与系统实现】
  • 【社论】跑赢12级狂风,敦煌做对了什么
  • 机关食堂向游客开放的重庆荣昌区,“消费市场迎来历史性突破”
  • 茹诗瑶评《失去伊斯坦布尔》︱“帝国主义者”的多重面相
  • 沪幼升小网上报名明起开始,是否参与民办摇号怎么定?
  • 长三角9座“万亿城市”一季报出炉:多地机器人产量大增
  • 韩国总统选举民调:共同民主党前党首李在明支持率超46%