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,那去掉 B 的 m方法试试。

去掉类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的基类A的 m方法。

上述程序调用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列表来进行查找的。