Python MRO机制详解:从旧式类到C3算法的演进

橘子海
橘子海
发布时间:2026-10-14 | 4人浏览过
原创

本文深入解析Python中的方法解析顺序(MRO),从单继承到多继承,详细对比旧式类深度优先搜索、新式类优化算法以及现代Python 3采用的C3线性化算法。通过具体代码示例和类图分析,阐明C3算法如何解决单调性问题,确保继承链的合理性与稳定性。

为何需要探讨MRO?

  • 在讨论多继承场景时,我们曾遇到一个核心问题:当子类继承的多个父类包含同名属性或方法时,子类实例调用该成员时,Python究竟会执行哪个父类的逻辑?
  • 这一行为的决定权,掌握在Python的MRO机制手中。

MRO的概念界定

  • MRO全称为Method Resolution Order,即方法解析顺序。
  • 对于单继承结构,MRO的逻辑非常直观:从当前类开始,沿着继承链逐级向上搜索父类,直到找到对应的属性或方法为止。
  • 因此,MRO的主要价值体现在多继承场景中,用于明确属性和方法的调用路径。
  • 在Python中,每个类都提供了一个内置属性__mro__,用于直接查看该类的MRO列表。

具体代码示例如下。


class A:
    def test(self):
        print("AAA-test")


class B:
    def test(self):
        print("BBB-test")


# 继承了三个类,B、A、还有默认继承的 object
class C(B, A):
    ...


# 通过类对象调用,不是实例对象!
print(C.__mro__)


# 输出结果 (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

  • 1. 方法搜索严格遵循__mro__属性输出的元组顺序,从左至右进行查找。
  • 2. 若在当前类(如Class C)中定位到目标方法,则立即执行并终止搜索。
  • 3. 若当前类未找到,则继续检查下一个父类(如Class B);若找到则执行并停止搜索。
  • 4. 若遍历至最后一个基类(Class object)仍未找到目标方法,程序将抛出异常报错。

类继承关系图示。

注意事项

MRO的实现背后涉及复杂的底层算法,下文将对此进行详细剖析。

MRO算法的演进历程

随着Python的发展,其MRO机制经历了三个主要阶段的算法迭代。

  • 旧式类MRO算法:采用从左至右的深度优先搜索(DFS)策略,这种早期实现被称为旧式类的MRO规则。
  • 新式类MRO算法:自Python 2.2版本起,新式类在深度优先搜索的基础上进行了优化改进。
  • C3算法:自Python 2.3版本引入,专门用于新式类;由于Python 3.x版本仅支持新式类,因此现代Python环境默认且仅使用C3算法。

旧式类与新式类的区别

Python学习之新式类和旧式类讲解

若需深入探究C3算法原理,可参考官方文档。

https://www.python.org/download/releases/2.3/mro/

旧式类MRO算法详解

注意:以下代码需在Python 2环境下运行以复现结果。

具体代码示例。


# 旧式类算法
class A:
    def test(self):
        print("CommonA")


class B(A):
    pass


class C(A):
    def test(self):
        print("CommonC")


class D(B, C):
    pass


D().test()


# python2 下的运行结果 CommonA

类继承结构图。

逻辑分析。

  • 通过类图可见,程序中四个类构成了典型的“菱形”继承关系。
  • 当使用D类实例调用test()方法时,依据深度优先算法,搜索路径为D->B->A->C->A。
  • 因此,旧式类MRO算法在A类中首先找到了test()方法,故最终输出结果为CommonA。

新式类MRO算法改进

  • 为修正旧式类MRO算法的缺陷,Python 2.2版本引入了新式类的MRO计算方式。
  • 该算法仍采用从左至右的深度优先遍历,但若遍历过程中遇到重复类,仅保留最后一次出现的该类。

结合上述代码示例分析。

  • 深度优先遍历的原始搜索顺序为D->B->A->C->A 。
  • 由于顺序中A类出现了两次,算法仅保留最后一个A。
  • 最终确定的搜索顺序为D->B->C->A。

新式MRO算法的局限性

尽管解决了旧式算法的部分问题,但新式算法仍可能违反单调性原则。

何谓单调性原则?

在子类存在多继承时,子类的MRO不应改变其父类的MRO搜索顺序,否则会导致程序运行异常。

具体代码示例。


class X(object):
    pass


class Y(object):
    pass


class A(X, Y):
    pass


class B(Y, X):
    pass


class C(A, B):
    pass

深度优先遍历后的原始搜索顺序为:C->A->X->object->Y->object->B->Y->object->X->object。

经过去重处理后的搜索顺序为:C->A->B->Y->X->object。

分析不同类的MRO结果。

  • A类:A->X->Y->object。
  • B类:A->Y->X->object。
  • C类:C->A->B->X->Y->object。

显而易见,B类与C类中X、Y的顺序相反,这意味着当B被继承时,其搜索顺序发生了改变,从而违反了单调性原则。

在Python 2中运行此代码会触发报错。

在Python 3中运行此代码同样会触发报错。

C3 MRO算法解决方案

  • 为彻底解决前述两种算法的缺陷,Python 2.3版本引入了C3方法来确定方法搜索顺序。
  • 在多数语境下,提及Python中的MRO,通常指代的便是C3算法。

将前述第一个示例代码置于Python 3环境中运行。


class A:
    def test(self):
        print("CommonA")


class B(A):
    pass


class C(A):
    def test(self):
        print("CommonC")


class D(B, C):
    pass


D().test()


# 输出结果 CommonC

C3算法基础解析

以上述代码为例,C3算法将各类的MRO等价转换为以下线性方程组。

  • A类:L[A] = merge(A , object)。
  • B类:L[B] = B + merge(L[A] , A)。
  • C类:L[C] = C + merge(L[A] , A)。
  • D类:L[D] = D + merge(L[B] , L[C] , B , C)。

概念解析:头与尾。

以A类为例,merge()函数处理列表中的第一个元素(如L[A]的头)构成L[A]的头部分,剩余元素(此处仅为object)称为尾部分。

merge运算的具体流程。

  • 1. 取出merge列表中第一个列表的头元素(如L[A]的头),记作H。
  • 2. 若H出现在merge其他列表的头部,则将其输出,并从所有列表中移除H。
  • 3. 若H仅出现在当前列表头部而未在其他列表头部出现,则将其输出,并从所有列表中移除H。
  • 4. 若H出现在merge其他列表的非头部位置,则跳过该列表,取下一个列表的头元素记作H,并返回步骤2。
  • 5. 重复执行上述步骤,直至列表为空。

若列表非空且无法再找出可输出的元素,则算法抛出异常。

简单类MRO计算示例。


class B(object): pass

print(B.__mro__)


(<class '__main__.B'>, <class 'object'>)
  • MRO计算过程演示。

L[B] = L[B(object)]
     = B + merge(L[object])
     = B + L[object]
     = B object

单继承MRO计算示例。


# 计算 MRO
class B(object): pass

class C(B): pass

print(C.__mro__)


(<class '__main__.C'>, <class '__main__.B'>, <class 'object'>)
  • MRO计算过程演示。

L[C] = C + merge(L[B])
     = C + L[B]
     = C B object

多继承MRO计算示例。


O = object

class F(O): pass

class E(O): pass

class D(O): pass

class C(D, F): pass

class B(D, E): pass

class A(B, C): pass


print(C.__mro__)
print(B.__mro__)
print(A.__mro__)


# 输出结果 (<class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class 'object'>) (<class '__main__.B'>, <class '__main__.D'>, <class '__main__.E'>, <class 'object'>) (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>)

  • O类与object类的MRO计算。

L[O] = O = object
  • D、E、F类的MRO计算。

L[D] = D + merge(L[O])
        = D O
  • C类的MRO计算。

L[C] = L[C(D, F)]
     = C + merge(L[D], L[F], DF)
     # 从前面可知 L[D] 和 L[F] 的结果
     = C +  merge(DO, FO, DF)
     # 因为 D 是顺序第一个并且在几个包含 D 的 list 中是 head,
     # 所以这一次取 D 同时从列表中删除 D
     = C + D + merge(O, FO, F)
     # 因为 O 虽然是顺序第一个但在其他 list (FO)中是在尾部, 跳过
     # 改为检查第二个list FO
     # F 是第二个 list 和其他 list 的 head
     # 取 F 同时从列表中删除 F
     = C + D + F + merge(O)
     = C D F O
  • B类的MRO计算。

L[B] = L[B(D, E)]
     = B + merge(L[D], L[E], DE)
     = B + merge(DO, EO, DE)
     = B + D + merge(O, EO, E)
     = B + D + E + merge(O)
     = B D E O
  • A类的MRO计算。

L[A] = L[A(B,C)]
        = A + merge(L[B], L[C], BC)
        = A + merge( BDEO, CDFO, BC )
        = A + B + merge( DEO, CDFO, C )
        # D 在其他列表 CDFO 不是 head,所以跳过到下一个列表的 头元素 C
        = A + B + C + merge( DEO, DFO )
        = A + B + C + D + merge( EO, FO )
        = A + B + C + D + E + merge( O, FO )
        = A + B + C + D + E + F + merge( O )
        = A B C D E F O

多继承MRO计算示例二。


O = object

class F(O): pass

class E(O): pass

class D(O): pass

class C(D, F): pass

class B(E, D): pass

class A(B, C): pass


print(C.__mro__)
print(B.__mro__)
print(A.__mro__)


# 输出结果 (<class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class 'object'>) (<class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class 'object'>) (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class 'object'>)

  • O类与object类的MRO计算。

L[O] = O = object
  • D、E、F类的MRO计算。

L[D] = D + merge(L[O])
        = D O
  • C类的MRO计算。

L[C] = L[C(D, F)]
        = C + merge(L[D], L[F], DF)
        = C + merge(DO, FO, DF)
        = C + D + merge(O, FO, F)
        = C + D + F + merge(O)
        = C D F O
  • B类的MRO计算。

L[B] = L[B(E, D)]
       = B + merge(L[E], L[D], ED)
       = B + merge(EO, DO, ED)
       = B + E + merge(O, DO, D)
       = B + E + D + merge(O)
       = B E D O
  • A类的MRO计算。

L[A]  = L[A(B, C)]
        = A + merge(L[B], L[C], BC)
        = A + merge(BEDO, CDFO, BC)
        = A + B + merge(EDO, CDFO, C)
        = A + B + E + merge(DO,CDFO, C)
        = A + B + E + C + merge(O,DFO)
        = A + B + E + C + D + merge(O, FO)
        = A + B + E + C + D + F + merge(O)
        = A B E C D F O

至此,关于Python MRO方法搜索顺序的讲解结束。如需了解更多Python MRO相关内容,请查阅本站过往文章或继续浏览后续教程。感谢大家的支持!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表