MRO 的全称是:Method Resolution Order,从 Python2.3 开始,Python 使用 C3 算法来实现 Python 类继承时的线性解析逻辑。

img

Python 使用 C3 作为 MRO 算法的起因

img

C3 算法主要解决的问题

img

C3 算法主要关注的特性

关于 MRO 的更多信息可以到 Python 官网查看:https://docs.python.org/zh-cn/3/howto/mro.html#python-2-3-mro

关于 C3 算法的解释可以查看:https://en.wikipedia.org/wiki/C3_linearization

当然也可以去搜索C3 算法相关论文:《A Monotonic Superclass Linearization for Dylan》

Python中如何查看 MRO 属性

查看 MRO

1
2
3
4
5
6
7
class A(object):
    def hello(self):
        print(self.__class__, 'A')

if __name__ == '__main__':
    print('A MRO:', A.mro())
    A().hello()

上面是最简单的类定义,通过调用 A.mro()即可以看到被线性化的类列表。

img

最简单的 MRO 列表

带有继承关系时的 MRO

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class A(object):
    def hello(self):
        print(self.__class__, 'A')

class B(A):
    def hello(self):
        print(self.__class__, 'B')

if __name__ == '__main__':
    print('B MRO:', B.mro())
    B().hello()

这里的继承比较简单直观,本身就是线性继承的,即 object –> A –> B,它的 MRO 长这样子:

img

线性继承

带有多重继承关系时的 MRO

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class A(object):
    def hello(self):
        print(self.__class__, 'A')

class B(object):
    def hello(self):
        print(self.__class__, 'B')

class C1(A, B):
    pass

class C2(B, A):
    pass

if __name__ == '__main__':
    print('C1 MRO:', C1.mro())
    C1().hello()

    print('C2 MRO:', C2.mro())
    C2().hello()

C1 和 C2 都继承了 A 和 B,只不过顺序不一样,此时他们的 MRO 和 hello 方法的执行逻辑发生了微妙的变化:

img

继承顺序与方法执行优先级关系

继承关系中的MRO决定了方法的查找顺序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class A(object):
    def hello(self):
        print(self.__class__, 'A')

class B(object):
    def hello(self):
        print(self.__class__, 'B')

class C1(A):
    pass

class C2(C1, B):
    pass

if __name__ == '__main__':
    print('C2 MRO:', C2.mro())
    C2().hello()

img

MRO 决定了成员函数的查找顺序

带有多重继承关系且具有公共基类时的 MRO

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class A(object):
    def hello(self):
        print(self.__class__, 'A')

class B(A):
    def hello(self):
        print(self.__class__, 'B')

class C1(A):
    pass

class C2(C1, B):
    pass

if __name__ == '__main__':
    print('C2 MRO:', C2.mro())
    C2().hello()

此时 C1 和 B 都继承自 A,而 C2 又分别继承了 C1 和 B,且 C1 自身没有实现 hello 方法,B 自身实现了 Hello 方法,此时的 MRO 为:

img

具有公共基类的 MRO

总结:MRO 在 Python 中的几个关键作用

  • 确定属性和方法的搜索顺序:当Python查找一个类的属性或方法时,它会按照MRO定义的顺序去各个父类中搜索。
  • 解决多重继承中的冲突:在多重继承中,可能会有多个父类定义了相同名称的属性或方法。MRO定义了一个明确的搜索顺序,以解决这些潜在的冲突。
  • 支持合作式多重继承:Python支持合作式多重继承,这意味着子类可以从多个父类中继承方法,MRO确保了这些方法被合理地调用。
  • 提高代码的可读性和可维护性:通过明确类的方法解析顺序,开发者可以更容易地理解和预测代码的行为,从而提高代码的可读性和可维护性。
  • 动态查找父类:super()函数使用MRO来动态地查找父类中的方法,确保了方法调用的正确性。
  • 支持类的动态特性:Python的类是动态的,可以在运行时修改。MRO帮助Python解释器在这些动态变化中正确地解析方法和属性。
  • 帮助实现多态:MRO确保了当子类重写父类方法时,调用super()将按照正确的顺序调用父类的方法,支持了多态的实现。