Python 新式类继承关系的 C3 算法(Python 2.3 的方法解析顺序,MRO)

翻译:刘硕

摘要:本文档面向于想要了解Python 2.3版本中 C3 方法解析顺序的 Python程序开发人员。尽管它对新手而言不是很友好,本文档里面还是提供了很多有助于理解的解决问题的例子。目前我还不知道有哪个公开的文档解决了类似的问题,因此本文档还是很有用的。

免责声明:

本人(指作者而非翻译者,翻译者按)将此文档以Python 2.3 许可证赠送给Python软件基金会(Python Software Foundation。照例,我需要提醒读者下面的内容应该是正确的,但是我不能给出任何保证。请擦亮眼睛,风险自负!

鸣谢:

感谢Python邮件列表中所有给我提供支持的人。感谢Paul Foley指出了很多微小瑕疵,并且建议我加入局部优先排序部分。感谢David Goodger帮忙排版了reStructuredText部分。感谢David Mertz帮忙编辑。感谢Joan G. Stark提供爬虫图片。最后,感谢Guido van Rossum热情地把本文档加入到Python2.3的官方主页中。

.-=-. .--.

__ .' '. / " )

_ .' '. / .-. \ / .-'\

( \ / .-. \ / / \ \ / / ^

\ `-` / \ `-' / \ `-` /

jgs`-.-` '.____.' `.____.'

我们开始吧

Felix qui potuit rerum cognoscere causas -- Virgilius

兴趣是最好的老师 -- 维吉尔

一切要从 Samuele Pedroni 在 Python 开发邮件列表提交的文章说起

首先,我要指出,我将要讨论的仅适用于在 Python 2.2 中引入的新式类:经典类保留了它们原来的方法解析顺序,深度优先,先左后右。因此,原有的经典类的代码不会收到影响;而且即便理论上来说 Python 2.2 的新式类的代码会被破坏,但是实际上 C3 解析顺序和 Python 2.2 中的方法解析顺序相差无几,所以基本也不会有什么影响。因此:

不必担心!

此外,除非你重度使用多继承,并且你有使用了非凡的继承,你是不必理解 C3 算法的,你可以直接跳过这篇文章。不过话说回来,如果你真的想要知道多继承究竟是如何实现的,那么这篇文章就是为你而写。好消息是,正如你期待的那样,这些原理并不是非常复杂。

让我们从一些基础的定义开始。

假设在一个负载的多继承关系中,有一个 C 类。有一个很重要的任务:我们要在这些复杂的继承关系中明确方法的执行顺序。例如,分辨出 C 类的继承顺序。

包括它本身咋内的 C 类继承顺序列表,以继承关系由近到远排序,被称作 C 类的优先级列表,或者线性化列表。

方法解析顺序(MRO)就是构造线性化列表的一串规则。在 Python 圈中,习惯用语“C 的MRO”的意义等同于 C 类的线性化列表。

例如,在单继承体系中,如果 C 类是 C1 类的子类,C1 类又是 C2 类的子类。那么 C 类的线性化列表就理所当然是 [C, C1 , C2]。然而,在多继承体系中,构造线性化列表的方式就要稍微复杂一点,因为构造一个同时满足 局部优先 和 单调 的线性化列表优点不容易。

我过会儿会讨论局部优先顺序,不过我要先在这里给出单调的定义。MRO 是单调的可以这样理解:在 C 类的线性化列表中,如果 C1 类排在 C2 类的前面,那么在任何一个 C 类的子类中, C1 类都要优先于 C2 类。如果MRO不满足单调性,无意间的衍生新类的操作就可能改变方法的解析顺序。这些改变会潜移默化地引入一些难以预测的 bug。过会儿会有这种 bug 出现的例子。

并不是所有的类都能实现线性化。在复杂的继承关系中会有很多不可能让派生出的新类的所有属性都满足线性化的情况。

我在这里给出一个这种情况例子。康康下面的继承:

>>> O = object

>>> class X(O): pass

>>> class Y(O): pass

>>> class A(X,Y): pass

>>> class B(Y,X): pass

上面的继承关系可以用下面的图示表示。我用 O 指代 object 类,它是一切新式类的始祖。

-----------

| |

| O |

| / \ |

- X Y /

| / | /

| / |/

A B

\ /

?

对于这种情况,不可能从 A 类和 B 类派生出一个新的 C 类。因为在 A 类中, X 类的优先级高于 Y 类,但是在 B 类中,Y 类的优先级高于 X 类。这样一来,C 类中的方法解析顺序就矛盾了。

对于这种情况,在 Python 2.3 中会抛出一个异常(TypeError: MRO conflict among bases Y, X,即:类型错误:基类 Y 和 X 方法解析顺序矛盾)来避免小白程序员制造矛盾的继承关系。然而 Python 2.2 不会抛出异常,而是选择一个 专门的 顺序(在这种情况下会是CABXYO)。

_ .-=-. .-==-.

{ } __ .' O o '. / -

{ } .' O'. / o .-. O \ / .--v`

{ } / .-. o\ /O / \ o\ /O /

\ `-` / \ O`-'o / \ O`-`o /

jgs `-.-` '.____.' `.____.'

C3 方法解析顺序

我来介绍几个在接下来的讨论中很有用的简单符号。我会用这些简写符号

C1 C2 ... CN

来指代类的列表 [C1, C2, ... , CN].

列表的 head (头部)是它的第一个元素:

head = C1

它的 tail (尾部)是列表的其余部分:

tail = C2 ... CN.

我也会用这些符号

C + (C1 C2 ... CN) = C C1 C2 ... CN

来表示列表的加和 [C] + [C1, C2, ... ,CN].

现在我可以解释 Python 2.3 中,MRO 是如何运作的了。

设想一个多重继承体系的 C 类,继承自基类 B1, B2, ... , BN。我们要计算出 C 类的线性化列表 L[C]。其规则如下:

C 类的线性化列表是 C 类与其融合(merge)后的所有父类线性化列表再加上所有父类组成新列表的和。

用符号来表示就是:

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

特别地,如果 C 类是没有父类的 object 类,它的线性化列表就很简单了:

L[object] = object.

然而,通常下还是要用下面的规则来计算融合(merge)的结果:

首先取出列表的头部(head),例如 L[B1][0];如果这个头部没有在其他任何列表的尾部(tail),就把它加入到 C 类的线性化列表中,并且把它从融合(merge)中全部移除。否则就查看并移除下一个列表的头部,如果它是一个符合条件的头部的话。重复操作直到所有的类都被移除或者不可能找到合适的头部了。在这种情况下,不可能进行融合,Python 2.3 会拒绝创建 C 类,并且抛出一个异常。

这个规则确保了融合操作 保留 顺序,如果顺序可以被保留的话。另一方面,如果顺序无法被保留(就像前面的一串顺序矛盾的例子),融合将无法计算。

如果 C 类只有一个父类(单继承)的融合是非常容易计算的。在这种情况下:

L[C(B)] = C + merge(L[B],B) = C + L[B]

然而,多继承的情况就变得稍微复杂一点。我觉得如果不举几个例子的话,你们是没办法理解的

python新式类c3算法_Python 新式类继承关系的 C3 算法(Python 2.3 的方法解析顺序,MRO)...相关推荐

  1. python新式类c3算法_Python新式类的方法解析顺序MRO与Super

    新式类与经典类的方法解析顺序 MOR(方法解析顺序) 经典类:深度优先 DFS python3以前 新式类:广度优先 python2.2 新式类:广度优先的C3算法实现(拓扑排序) BFS pytho ...

  2. mro python_Python新式类的方法解析顺序MRO与Super

    新式类与经典类的方法解析顺序 MOR(方法解析顺序) 经典类:深度优先 DFS python3以前 新式类:广度优先 python2.2 新式类:广度优先的C3算法实现(拓扑排序) BFS pytho ...

  3. python类中方法的执行顺序-浅谈Python的方法解析顺序(MRO)

    方法解析顺序, Method Resolution Order 从一段代码开始 考虑下面的情况: class A(object): def foo(self): print('A.foo()') cl ...

  4. python方法解析顺序_浅谈Python的方法解析顺序(MRO)

    方法解析顺序, Method Resolution Order 从一段代码开始 考虑下面的情况: class A(object): def foo(self): print('A.foo()') cl ...

  5. python面向对象--方法解析顺序(MRO)

    转载:https://www.cnblogs.com/qunxiadexiaoxiangjiao/p/8311429.html 对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于 ...

  6. Python的方法解析顺序(MRO)变化过程

    MRO,即 Method Resolution Order,是继承中确定调用哪个方法(属性)的搜索顺序方法. 对于只支持单继承的语言(Java和C#)来说,MRO 一般比较简单:而对于 C++, Py ...

  7. 图解Python 3.x多继承时方法解析顺序MRO

    在Python 3.x的多继承树中,如果在中间层某类有向上一层解析的迹象,则会先把本层右侧的其他类方法解析完,然后从本层最后一个解析的类方法中直接进入上一层并继续解析,也就是在从子类到超类的反向树中按 ...

  8. python类中方法的执行顺序-python – 新式类中的方法解析顺序(MRO)?

    在Python in a Nutshell(第2版)一书中有一个使用的例子 旧样式类,用于演示如何以经典分辨率顺序解析方法 它与新订单有何不同. 我通过重写新样式的示例尝试了相同的示例,但结果与使用旧 ...

  9. python---方法解析顺序MRO(Method Resolution Order)<以及解决类中super方法>

    python---方法解析顺序MRO(Method Resolution Order)<以及解决类中super方法> 参考文章: (1)python---方法解析顺序MRO(Method ...

最新文章

  1. NYOJ 928 小M的因子和(数论)
  2. 电容的q值计算公式_在设计电路中电容容量大小、耐压等级选取详解 (转)
  3. tensorflow笔记2:TensorBoard
  4. 最简单的基于FFMPEG的图像编码器(YUV编码为JPEG)
  5. 单片机c语言跑马灯实验报告,单片机跑马灯实验报告
  6. 3 | 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?
  7. 数字图像处理复习记录(二)邻接、连通和形态学处理
  8. xlsx格式表格汉字批量转音节,不带声调(python)
  9. 微信新表情真的太骚了!!
  10. python关于q检验
  11. Zabbix5.0如何发送短信
  12. TM卡 DS1990A读写程序
  13. H5 集成微信自定义分享(兼容 ios android)
  14. 第二个c程序,日语208音练习
  15. table的行和列的隐藏和显示
  16. php空间搭建装网盘,编译安装Nginx和php搭建KodExplorer网盘
  17. 圣母大学研究生申请要求
  18. SDNUOJ 1058 人名查询 (练习map的用法)
  19. java 异步线程池_Java - 异步线程池
  20. 温度传感器php,温度传感器基础知识介绍

热门文章

  1. 【python--爬虫】爬取网站美女照片
  2. 电子围栏的实现(一):矩形、圆与多边形的处理
  3. Movavi.Video.Editor.Business.15.5.0 下载安装和激活
  4. 有手就能学会的,通过提取系统Boot来获取Root权限
  5. PMBOK(第六版) PMP笔记——《第四章 项目整合管理》
  6. 关闭谷歌 Chrome 浏览器不再支持 Win7的置顶横条通知
  7. STM32CubeMX学习笔记(17)——电源管理(PWR)低功耗待机模式
  8. 魔塔之拯救白娘子~我的第一个VB6+DX8做的小游戏源码~3鼠标键盘和手柄引擎
  9. Java多线程模拟在同一张银行中取钱存钱
  10. 中国光博会开幕倒计时 -- 聚焦光电硬科技,八大亮点抢先看