编程语言
首页 > 编程语言> > Python - 面向对象编程 - MRO 方法搜索顺序

Python - 面向对象编程 - MRO 方法搜索顺序

作者:互联网

为什么会讲 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 发展到现在经历了三种算法

  1. 旧式类 MRO 算法:从左往右,采用深度优先搜索(DFS),从左往右的算法,称为旧式类的 MRO
  2. 新式类 MRO 算法:自 Python 2.2 版本开始,新式类在采用深度优先搜索算法的基础上,对其做了优化
  3. C3 算法:自 Python 2.3 版本,对新式类采用了 C3 算法;由于 Python 3.x 仅支持新式类,所以该版本只使用 C3 算法

 

什么是旧式类,新式类

https://www.cnblogs.com/poloyy/p/15226425.html

 

想深入了解 C3 算法的可以看看官网

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

 

旧式类 MRO 算法

需要在 python2 环境下运行这段代码

 

实际代码

# 旧式类算法
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

 

类图

 

分析

 

新式类 MRO 算法

 

以上面的代码栗子来讲

 

新式 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

 

分析不同类的 MRO

很明显,B、C 中间的 X、Y 顺序是相反的,就是说 B 被继承时,它的搜索顺序会被改变,违反了单调性

 

在 python2 中运行这段代码的报错

 

在 python3 中运行这段代码的报错 

 

C3 MRO 算法

 

将上面第一个栗子的代码放到 python3 中运行

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 类为栗,merge() 包含的 A 成为 L[A] 的头,剩余元素(这里只有 object)称为尾

 

merge 的运算方式

  1. 检查 merge 列表的头元素(如 L[A] 的头),记作 H
  2. 若 H 未出现在 merge 表达式中其他列表的尾部,则将其输出,并将其从所有列表中删除
  3. 然后回到步骤一,取出下一个列表的头元素记作 H

重复以上步骤直到列表为空,则算法结束;如果不能再找出可以输出的元素,则抛出异常

 

简单的类计算 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

 

标签:Python,object,merge,算法,MRO,面向对象编程,pass,class
来源: https://www.cnblogs.com/poloyy/p/15226424.html