6. 方法解析顺序MRO

钻石继承(菱形继承)问题

示例

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

上述程序的继承关系图:

  object
    |
    A
   / \
  B   C
   \ /
    D

上述程序构成了钻石继承的基本要素。下面我们为ABCD四个类添加方法 m 如下:

# 方法解析顺序MRO 示例
# https://weimingze.com

class A:
    'A类'
    def m(self):
        print('A')

class B(A):
    'B类'
    def m(self):
        print('B')

class C(A):
    'C类'
    def m(self):
        print('C')

class D(B, C):
    'D类'
    def m(self):
        print('D')

d = D()
d.m()  # 调用哪个方法?

上述程序打印结果为: D

多继承说明

去掉类D的方法m,如下

# 方法解析顺序MRO 示例
# https://weimingze.com

class A:
    'A类'
    def m(self):
        print('A')

class B(A):
    'B类'
    def m(self):
        print('B')

class C(A):
    'C类'
    def m(self):
        print('C')

class D(B, C):
    'D类'

d = D()
d.m()  # 调用哪个方法?

上述程序打印结果为: B,此时 B 的基类是 A,那去掉 Bm 方法试试。

去掉类 B 的方法 m,如下.

# 方法解析顺序MRO 示例
# https://weimingze.com

class A:
    'A类'
    def m(self):
        print('A')

class B(A):
    'B类'

class C(A):
    'C类'
    def m(self):
        print('C')

class D(B, C):
    'D类'

d = D()
d.m()  # 调用哪个方法?

上述程序打印结果为: C,并不是调用 B 的基类 Am 方法。

上述程序调用 m 方法遵循的是方法解析顺序 MRO

什么是方法解析顺序

方法解析顺序(Method Resolution Order, MRO)是在多继承场景下确定方法调用顺序的规则。

作用:

当类继承自多个父类,且这些父类中有同名方法时,MRO 决定了 Python 解释器查找方法的顺序。

__mro__属性

mro 属性绑定一个元组,此元组记录的是此子类的所有基类。基类的先后顺序表示方法的查找次序。

super() 函数就是依据此属性对父类的方法进行查找的。

示例:

# 方法解析顺序MRO 示例
# https://weimingze.com

class A:
    'A类'
    def m(self):
        print('A')

class B(A):
    'B类'
    def m(self):
        print('B')

class C(A):
    'C类'
    def m(self):
        print('C')

class D(B, C):
    'D类'
    def m(self):
        print('D')

d = D()
d.m()  # 调用哪个方法?
print(D.__mro__)

打印结果是

D
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

这个元组就是 MRO 列表,他的先后顺序也是 D 类的方法查找顺序。

super() 函数在调用父类方法时,它也是依赖 MRO 列表来进行查找的。

视频讲解