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

2025-10-06 Python不基础13——mro

文章目录

  • 1. MRO定义与作用
    • 1.1. 案例1:单继承
    • 1.2. 案例2:多层单继承
    • 1.3. 案例3:多继承
    • 1.4. 案例4:复杂多继承
  • 2. MRO两种获取方式
    • 2.1. `__mro__`属性(返回元组)
    • 2.2. `mro()`方法(返回列表)
  • 3. C3线性化算法
    • 3.1. 局部优先顺序(Local Precedence Order)
    • 3.2. 单调性(Monotonicity)
    • 3.3. 扩展优先图(Extended Precedence Graph)
  • 4. MRO冲突
  • 5. 总结

本文参考视频链接:

  • https://www.bilibili.com/video/BV1V5411S7dY

MRO(Method Resolution Order,方法解析顺序)是Python解决多继承场景下方法/属性调用优先级的核心机制。在Python支持的多继承中,当子类调用一个方法/属性时,可能存在多个父类定义了该方法/属性——此时需要明确“优先从哪个父类查找”。

MRO的本质就是将子类的所有父类(包括间接父类)线性化成一个优先级队列,调用时按队列顺序依次查找,直到找到目标方法/属性。

1. MRO定义与作用

通过4个递进案例,直观理解MRO如何解决不同继承结构下的方法调用优先级问题。

1.1. 案例1:单继承

class A:def c(self):print("A")class M(A):  # M仅继承A(单继承)passm = M()
m.c()  # 调用结果:A
  • 单继承下,M的MRO队列是[M, A, object]object是所有类的默认父类);
  • 调用m.c()时,按顺序先查M(无c)→ 再查A(有c)→ 执行A的c,结果为“A”。

1.2. 案例2:多层单继承

class A:def c(self):print("A")class B(A):  # B继承Adef c(self):print("B")class M(B):  # M继承B(间接继承A)passm = M()
m.c()  # 调用结果:B
  • M的MRO队列是[M, B, A, object]
  • 查找顺序:M(无)→ B(有c)→ 执行B的c,结果为“B”(优先查直接父类,再查间接父类)。

1.3. 案例3:多继承

class A:def c(self):print("A")class B:def c(self):print("B")class M(A, B):  # 继承顺序:A在前,B在后passm = M()
m.c()  # 调用结果:A
class M(B, A):  # 继承顺序:B在前,A在后passm = M()
m.c()  # 调用结果:B
  • 多继承下,MRO优先遵循“继承列表的顺序”(写在前面的父类优先级更高);
  • 对应的MRO队列分别为[M, A, B, object][M, B, A, object],因此调用结果随继承顺序变化。
M1
A1
B1
M2
B2
A2

1.4. 案例4:复杂多继承

class A:def c(self):print("A")class C(A):  # C继承Apassclass B:  # B不继承Adef c(self):print("B")class M(C, B):  # M继承C、Bpassm = M()
m.c()  # 调用结果:A
class B(A):  # 新增:B继承Adef c(self):print("B")class M(C, B):  # 继承顺序不变:C在前,B在后passm = M()
m.c()  # 调用结果:B
  • 案例1的MRO队列:[M, C, A, B, object] → 查M→C→A(有c)→ 结果“A”;
  • 案例2的MRO队列:[M, C, A, B, object]?不,实际是[M, C, B, A, object](因B也继承A,C和B的共同父类是A,需调整顺序)→ 查M→C(无c)→ B(有c)→ 结果“B”;
  • 核心:复杂继承下,MRO不能仅看直接继承顺序,还需考虑间接父类的依赖关系,这就需要底层算法(C3)计算。
M1
C1
A1
B1
M2
C2
A2
B2

MRO作用

  1. 解决多继承歧义:明确方法/属性的查找顺序,避免“多个父类有同名方法时该调用哪个”的问题;
  2. 线性化继承关系:将“子类→直接父类→间接父类”的树形继承结构,转化为有序列表(MRO队列),查找时按列表顺序执行;
  3. 保证一致性:确保同一继承结构下,方法查找顺序唯一且符合直觉(由C3算法保证)。

2. MRO两种获取方式

Python为每个类提供了两种直接获取MRO队列的方式,无需手动计算,适用于任何继承结构。

2.1. __mro__属性(返回元组)

  • 语法:类名.__mro__
  • 返回值:包含类本身、所有父类(直接/间接)、object的元组,顺序即MRO查找顺序。
print(M.__mro__)
# 输出:(<class '__main__.M'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

2.2. mro()方法(返回列表)

  • 语法:类名.mro()
  • 返回值:与__mro__内容一致,但格式为列表(更易操作,如索引访问)。
print(M.mro())
# 输出:[<class '__main__.M'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
  • 新式类(继承object的类,Python 3中所有类默认是新式类)支持这两种方式;
  • MRO队列的第一个元素永远是类本身,最后一个永远是object(所有类的顶层父类)。

3. C3线性化算法

Python 2.3版本后,MRO的计算统一采用C3线性化算法(C3 Linearization)。该算法由1996年提出,最初用于Dylan语言,核心是“在三个关键属性上保证一致性(Consistent)”,因此命名为“C3”。

image-20251006145955864

C3算法通过严格的规则,确保MRO队列同时满足以下三个属性,从而避免歧义且符合直觉。视频从“易到难”拆解了这三个属性:

3.1. 局部优先顺序(Local Precedence Order)

  1. 子类继承多个父类时,继承列表中写在前面的父类,在MRO中优先级更高(对应案例3的“继承顺序影响结果”);
  2. 该优先级需“传递”给子类的子类:若父类A在父类B之前,则所有继承该子类的后代类,MRO中A的优先级仍高于B。
# 父类:A和B无继承
class A:def c(self):print("A")
class B:def c(self):print("B")# 子类M1:继承顺序A→B,A优先
class M1(A, B):pass
print(M1.mro())  # [M1, A, B, object]# 子类M2:继承M1,需保持A在B前
class M2(M1):pass
print(M2.mro())  # [M2, M1, A, B, object](A仍在B前)
M2
M1
A
B
  • 关键结论:局部优先顺序是“写在前面即优先”,且该规则在继承链中不可打破。

3.2. 单调性(Monotonicity)

子类不能跳过直接父类,去调用间接父类的方法。即:若父类D的MRO中,方法c来自父类X,则子类M(继承D)的MRO中,c也必须来自X(或D本身),不能来自比X优先级更低的类。

# 继承结构:D继承C、B;C继承A;B无继承
class A:def c(self):print("A")
class C(A):pass
class B:def c(self):print("B")
class D(C, B):  # D的MRO:[D, C, A, B, object],c来自Apass
class M(D):     # M的直接父类是Dpass
print(M.mro())  # [M, D, C, A, B, object]
m = M()
m.c()  # 结果:A(与D的c来源一致,未跳过D寻找B)
M
D
C
A:c()
B:c()
  • 若M的MRO变为[M, D, B, A, object],则M的c来自B,而D的c来自A——M跳过了D的方法来源(A),违反单调性。C3算法会避免这种情况。
  • 单调性较难理解,实际编程中“只要不设计极端复杂的继承结构”,即可避免违反;若听不懂,可暂时聚焦“获取MRO结果”而非原理。

3.3. 扩展优先图(Extended Precedence Graph)

对于两个无直接继承关系的父类X和Y,若它们存在最小公共子类(即同时继承X和Y的最底层子类),则X和Y的优先级由“最小公共子类的继承顺序”决定。

图表案例

M
D
A
E
C
B
  1. 类结构:
    • D(B, A):继承顺序“B在前”;
    • E(C, A):继承顺序“C在前”;
    • M(D, E):最小公共子类(同时继承B和C的最底层类)。
  2. 优先级判断:
    • 最小公共子类M的继承顺序是“D在前,E在后”;
    • DB的子类,EC的子类;
    • 因此,在M的MRO中,B的优先级高于C

这是三个属性中最难的,核心是“通过最小公共子类的继承顺序,确定间接父类的优先级”,实际编程中极少需要手动分析,了解即可。

4. MRO冲突

当继承结构违反C3算法的规则时,Python会在编译阶段直接报错,无法生成MRO队列——因为不存在同时满足三个属性的线性化顺序。

冲突案例

# 矛盾的继承结构
class A:pass
class B:pass
class C(A, B):  # C的MRO:[C, A, B, object],A优先于Bpass
class D(B, A):  # D的MRO:[D, B, A, object],B优先于Apass
class M(C, D):  # M同时继承C和D,冲突!passTypeError: Cannot create a consistent method resolution order (MRO) for bases A, B

冲突原因

  • C要求“M的MRO中A在B前”,D要求“M的MRO中B在A前”;
  • 两者矛盾,C3算法无法生成同时满足的MRO队列,因此Python直接拒绝创建类M

5. 总结

  1. 定义:MRO是多继承下方法/属性的查找顺序,本质是“继承结构的线性化列表”;
  2. 获取:通过类名.__mro__(元组)或类名.mro()(列表)直接获取;
  3. 算法:Python 2.3+用C3线性化算法,确保MRO满足“局部优先、单调性、扩展优先图”三个属性;
  4. 冲突:矛盾的继承结构会导致MRO生成失败,Python直接报错;
  5. 实践:简化继承、查MRO结果、谨慎使用多继承,是避免MRO问题的关键。

通过理解MRO,可清晰解决多继承下的方法调用歧义,写出更健壮的Python类代码。

优先简化继承结构

  • 多继承的复杂度与MRO的计算难度正相关,尽量采用“单继承+组合”替代多继承(如用Mixin类时,确保Mixin类功能单一,且继承顺序清晰);
  • 例:若需同时实现“滚动”和“编辑”功能,优先用class MyPane(B, C, Pane)(Mixin类在前,主类在后),避免多层嵌套的交叉继承。

学会“查结果”而非“算过程”

  • 无需手动计算MRO(C3算法复杂),编写继承代码后,用__mro__mro()查看实际查找顺序;
  • 例:定义类后先执行print(类名.mro()),确认方法查找顺序符合预期,再编写业务逻辑。

理性看待多继承

  • 争议:部分程序员认为“多继承是原罪”(易导致MRO冲突和代码混乱),因此Java等语言仅支持单继承;
  • Python态度:允许多继承,但需谨慎使用——合理的多继承(如功能单一的Mixin类)能提高代码复用,但滥用会导致维护困难;
  • 核心原则:若使用多继承,确保“每个父类的职责明确”“继承顺序可解释”,且通过MRO验证无冲突。
http://www.dtcms.com/a/450333.html

相关文章:

  • 那片海dede织梦源码企业网络公司工作室网站模板源码模板php网页游戏维京传奇
  • 【深度学习03】神经网络基本骨架、卷积、池化、非线性激活、线性层、搭建网络
  • 新媒体营销seo个人优化方案案例
  • Redis项目应用总结(苍穹外卖/黑马头条/乐尚代驾)
  • 做网站js还是jq2021年世界500强榜单
  • 建设旅游网站的费用预算杭州抖音代运营
  • 【LaTeX】 13 LaTeX 长文档结构管理
  • Python入门:Python3基础练习题详解
  • 高端网站建设加盟帮人做彩票网站
  • 哪个网站做的ppt模板好查查企业网
  • 为什么做的网站别的浏览器打不开怎么回事做网站规划
  • 做影视后期应该关注哪些网站做神马网站优化快速
  • 测试题——1
  • 力扣3634. 使数组平衡的最少移除数目
  • 网站服务器不稳定樟木头网站建设
  • 建设网站都需投入哪些资源wordpress没有图片
  • 网站主栏目投资网站排行
  • 国内永久crmseo刷关键词排名免费
  • 爬虫的道德与法律边界:Robots 协议、版权与个人信息保护
  • @[TOC](文件操作和IO)
  • 打开网站不要出现 index.html携程网站联盟
  • 律师行业协会网站建设做简历的网站叫什么
  • c++ enum和enum class
  • 罗湖网站制作多少钱南京企业网站
  • 【论文阅读】通义实验室,VACE: All-in-One Video Creation and Editing
  • 2025年--Lc165--H637.二叉树的层平均值(二叉树的层序遍历)--Java版
  • 消息顺序消费问题
  • 网站实名制查询唐山网站设计制作
  • 怎么给网站加速长沙房价2020最新价格
  • LeetCode:99.下一个排列