资讯专栏INFORMATION COLUMN

Python: 多继承模式下 MRO(Method Resolution Order) 的计算方式

golden_hamster / 1109人阅读

摘要:大家可能已经知道了,在的新式类中多继承模式是使用算法来确定的。那么具体是怎么计算的呢本文将基于中的几个例子来讲解是怎么计算的。下面再来个上面那些类的变种,可以先自己算算看,后面有详细的计算过程。

大家可能已经知道了,在 Python 3(Python 2 的新式类)中多继承模式是使用 C3 算法来确定 MRO(Method Resolution Order) 的。

那么具体是怎么计算的呢?本文将基于 https://www.python.org/downlo... 中的几个例子来讲解 MRO 是怎么计算的。

我们首先来定义一些符号: :

用 CN 表示一个类:C1, C2, C3, ..., CN
C1 C2 C3 ... CN 表示的是一个包含多个类的列表 [C1, C2, C3, ..., CN]

其中: :

head = C1
tail = C2 ... CN

加法运算: :

C + (C1 C2 ... CN) = C C1 C2 ... CN
[C] + [C1, C2, ... ,CN] = [C, C1, C2, ..., CN]

L[C] 表示类 C 的线性值,其实就是 C 的 MRO, 其中 :

L[object] = object

比如有个类 : :

class C(B1, B2, ..., BN): pass

那么: :

L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)

merge 的计算规则如下:

take the head of the first list, i.e L[B1][0]; if this head is not in the tail of any of the other lists, then add it to the linearization of C and remove it from the lists in the merge, otherwise look at the head of the next list and take it, if it is a good head. Then repeat the operation until all the class are removed or it is impossible to find good heads. In this case, it is impossible to construct the merge, Python 2.3 will refuse to create the class C and will raise an exception.

计算 MRO

先从简单的类说起: :

class B(object): pass

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

>>> B.mro()
[, ]

简单的子类: :

class C(B): pass

L[C] = L[C(B)]
     = C + merge(L[B])
     = C + L[B]
     = C B object     # 从上面已经知道了 L[B] = B object

>>> C.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

很容易就可以想到: :

L[O] = O = object
L[F] = L[F(O)] = F  O
L[E] = L[E(O)] = E  O
L[D] = L[D(O)] = D  O

下面来计算 C, B, A:

L[C]: :

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)中不是 head, 跳过,
     # 改为检查第二个list FO # F 是第二个 list 和其他 list 的 head, 
     # 取 F同时从列表中删除 F
     = C + D + F + merge(O)
     = C D F O

>>> C.mro()
[, , , ]

L[B]: :

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

>>> B.mro()
[, , , ]

L[A]: :

L[A] = L[A(B, C)]
     = A + merge(L(B), L(C), BC)
     = A + merge(BDEO, CDFO, BC)
     = A + B + merge(DEO, CDFO, C)
     # 注意这里是 C , 因为第一个list 的 head D 不是其他list 的 head
     # 所以改为从下一个 list CDFO 开始
     = 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

>>> A.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

跟之前唯一的区别是 B(D, E) 变成了 B(E, D) :

L[O] = O = object
L[F(O)] = F  O
L[E(O)] = E  O
L[D(O)] = D  O

L[C] = L[C(D, F)]
     = C + merge(L[D], L[F], DF)
     = C D F O

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
>>> B.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(DO, DFO)
     = A + B + E + C + D + merge(O, FO)
     = A + B + E + C + D + F + merge(O)
     = A B E C D F O
>>> A.mro()
[, , ,
 , , , ]

通过这几个例子应该对如何计算 MRO 已经有所了解了,更详细的信息可以阅读 python MRO 文档 以及 wikipedia 中的 C3 算法.

参考资料

The Python 2.3 Method Resolution Order | Python.org

C3 linearization - Wikipedia

原文地址: https://mozillazg.com/2016/11...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/44329.html

相关文章

  • Python继承

    摘要:中使用多继承,会涉及到查找顺序重复调用钻石继承,也叫菱形继承问题等即,用于判断子类调用的属性来自于哪个父类。本地优先级指声明时父类的顺序,比如,如果访问类对象属性时,应该根据声明顺序,优先查找类,然后再查找类。 python中使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承,也叫菱形继承问题)等 MRO MRO即method resolution order,用于判断子类调用...

    crossea 评论0 收藏0
  • Python OOP 面向对象编程

    摘要:时代,如果需要手动继承,如多态多态是指,不同的子类对象调用相同的父类方法,会产生多态多样结果的编程特性。 参考:黑马程序员教程 - Python基础 面向对象 OOP三大特性,且三个特性是有顺序的: 封装 继承 多态 封装 指的就是把现实世界的事务,封装、抽象成编程里的对象,包括各种属性和方法。这个一般都很简单,不需要多讲。 唯一要注意的就是:推荐从小往大开始封装、开发类。比如手枪...

    weknow619 评论0 收藏0
  • 5分钟了解 Pythonsuper函数是如何实现继承

    摘要:和中有一个很大的区别就是类,无论是类的定义还是类的继承。中类的继承可以直接使用关键字代替原来的。中注释掉了函数,所以并没有向后继续执行。如果中仍然有函数则会继续向上去查找中是否有函数。 showImg(https://img-blog.csdnimg.cn/20190306054027681.png?x-oss-process=image/watermark,type_ZmFuZ3po...

    psychola 评论0 收藏0
  • Python继承优缺点

    摘要:本文重点不要试图在内置类型的子类中重写方法,可以继承的可拓展类寻求变通掌握多重继承中的和了解处理多重继承的一些建议。子类化的代码如下输出小结上述问题只发生在语言实现的内置类型子类化情况中,而且只影响直接继承内置类型的自定义类。 导语:本文章记录了本人在学习Python基础之面向对象篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 本文重点: 1、不要试图在内置...

    Sourcelink 评论0 收藏0
  • Python super()就是这样

    摘要:所以本文中使用的是新式类,而新式类的搜索算法是算法上面的代码中是父类,是子类,我们在类重新定义了方法,就是在类的功能基础基础上新添功能。在这里的作用就是在子类中调用父类的方法,这个也是在单继承常见调用的用法。 python的类分别有新式类和经典类,都支持多继承。在类的继承中,如果你想要重写父类的方法而不是覆盖的父类方法,这个时候我们可以使用super()方法来实现 注意 Python2...

    laznrbfe 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<