Python是支持面向对象编程的,同时也是支持多重继承的。而支持多重继承,正是Python的方法解析顺序(Method Resoluthion Order, 或MRO)问题出现的原因所在。

python中至少有三种不同的MRO:

经典类(calssic class),深度优先遍历

在python2.2中提出了type和class的统一,出现了一个内建类型以及自定义类的公共祖先object,即新式类(new-style class)预计算

python2.3之后新式类的C3算法,这是python3唯一支持的方式

经典类MRO

在python2中存在两种类,经典类和新式类,而新式类都继承于object。在python2.1之前,经典类是唯一的选择,python3之后新式类是唯一的选择,而python2.2~python2.7则可以混杂使用,但推荐使用新式类。

经典类在处理MRO是简单明了:从左到又深度优先。但这种MRO在处理钻石继承时会出现一些bug:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# coding: utf-8

import inspect

class A:

def method(self):

print "A.method"

class B(A):

pass

class C(A):

def method(self):

print "C.method"

class D(B, C):

pass

print inspect.getmro(D)

x = D()

x.method()

# output

# (, , , )

# A.method

这显然不是我们希望的结果,在C中我们覆盖了A中method的实现,所以当我们从C继承时,在实例D并调用method时,我们希望D.method调用的是C.method而不是A.method。按照从左到右深度优先的方法,方法查找顺序为[D,B, A, C],所以当x在A找到了method时即直接返回,假如要使得x能调用C的metod,必须得调整D继承对象的顺序为

1

D(C,B):pass

新式类的MRO

python2.2新式类中所有的类都有共同的祖先-object,所以不能再简单的采用经典类的MRO,因而新式类中引进了一种新式的MRO推算方法,在定义类时,将该类的MRO作为类的属性,因而新式类可以通过mro直接获取类的MRO。

python2.2新式类在计算MRO时和经典类采用相似的计算方式:从左到右,深度优先,如果遍历中出现重复的类,只保留最后一个。还是以之前的例子。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# coding: utf-8

class A(object):

def method(self):

print "A.method"

class B(A): pass

class C(A):

def method(self):

print "C.method"

class D(B, C): pass

print D.__mro__

x = D()

x.method()

# output

# (, , , , )

# C.method

按照深度优先遍历,顺序为[D, B, A, object, C, A, object],重复的类只保留最后一个,则查找顺序为[D, B, C, A, object],查找顺序和我们期望的一致。

但是这种计算方法对稍微复杂一点的钻石继承问题有种无力感,譬如类型冲突:

1

2

3

4

5

6

7

8

9

class X(object): pass

class Y(object): pass

class A(X, Y): pass

class B(Y, X): pass

class C(A, B): pass

这段代码的矛盾之处在于,从C的角度,查找顺序应该为[C, A, X, object, B, Y, object, X, object],即[C, A, B, Y, X, object],实际上输出的结果为[C, A, B, X, Y, object],为何?

再看看C父类的解析顺序,对A来说,顺序为[A, X, Y, object],而对B来说,顺序为[B, Y, X, object],问题到这里就有矛盾了,A和B中X,Y的MRO搜索顺序刚好相反!所以无论在C中如何调整X,Y的顺序,A或B被C继承时,本身MRO查找的行为也发生了改变,这种继承会引发潜在的bug。对于这个问题,在python2.3发行时有了一官方文档来讨论这个问题,其中有一个结论是:

A MRO is monotonic when the following is true: if C1 precedes C2 in the linearization of C, then C1 precedes C2 in the linearization of any subclass of C. Otherwise, the innocuous operation of deriving a new class could change the resolution order of methods, potentially introducing very subtle bugs.

翻译过来大概是:MRO应该是单调的,在C类的查找顺序中,如果C1线性顺序先于C2,则在C的任何子类中,C1的线性顺序先于C2,假如子类无意中改变了父类的查找顺序,会引发潜在的错误。

但是在python2.2上面这段代码并没有报错,幸运的是,今天已经很少人还在使用着python2.2这种古老版本的python。

C3 superclass linearization

python2.3之后,C3算法便是唯一用来确认新类的方法解析顺序的算法了。C3算法就是,C的线性化是C加上父类的线性化和父类列表的总和,即:

1

L[SubClass(Base1, Base2, ..., BaseN)] = [SubClass] + merge(L[base1], ..., L[baseN], Base1, ..., BaseN)

其中在L[C] = [C1, C2, C3, …, CN]中,我们称[C1]为L[C]的表头,而[C2, …, CN]为表尾。其中merge是一个递归的过程:

检查第一个列表的头元素H

假如H未出现在其他列表的表尾,将其输出,并从其他列表中删除,并返回步骤1。否则,取出下一个列表的表头做H,继续步骤2。

重复上述步骤到列表为空,算法结束,或者不能再找出可以输出的元素,则说明无法构建继承关系,python会抛出异常。

还是以刚才复杂钻石继承为例子,我们分析一下A的线性化计算:

1

2

3

4

5

6

7

8

L[object] = [object]

L[X] = [X, object]

L[Y] = [Y, object]

L[A] = [A] + merge(L[X], L[Y], [X], [Y])

= [A] + merge([X, object], [Y, object], [X], [Y])

= [A, X] + merge([object], [Y, object], [Y])

= [A, X, Y] + merge([object], [object])

= [A, X, Y, object]

同样的可以推导出B:

1

L[B] = [B, Y, X, object]

最后来看一看C的线性化计算结果。

1

2

3

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

= [C] + merge([A, X, Y, object], [B, Y, X, object], [A], [B])

= [C, A, B] + merge([X, Y, object], [Y, X, object])

到最后一步计算便陷入了死循环了,X是第一个表的表头同时是第二个表表尾,Y是第二个表表头但是第一个表表尾,因此python只能报错,因为如果不调整A或B继承顺序,无法构建一个无二义性的继承关系。

1

2

3

4

5

6

7

8

9

10

11

class X(object): pass

class Y(object): pass

class A(X, Y): pass

class B(X, Y): pass

class C(A, B): pass

print C.__mro__

调整后我们来计算,

1

2

3

4

5

6

7

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

= [C] + merge([A, X, Y, object], [B, X, Y, object], [A], [B])

= [C, A] + merge([X, Y, object], [B, X, Y, object])

= [C, A, B] + merge([X, Y, object], [X, Y, object])

= [C, A, B, X] + merge([Y, object], [Y, object])

= [C, A, B, X, Y] + merge([object], [object])

= [C, A, B, X, Y, object]

验证一下正确性:

1

(, , , , , )

python mro文件_Python面向对象之MRO相关推荐

  1. python mro文件_Python MRO

    文中代码基于Python3.7 对于Python中的多继承情况,运行时在搜索对象的属性或方法时,需要遵循一定的顺序规则,这个规则称为:Method Resolution Order (MRO). MR ...

  2. python打开文件_python如何学习

    1.Python 文件I/O 本章只讲述所有基本的 I/O 函数,更多函数请参考Python标准文档. 打印到屏幕 最简单的输出方法是用print语句,你可以给它传递零个或多个用逗号隔开的表达式.此函 ...

  3. python遍历文件_python遍历目录文件脚本的示例

    例子 自己写的一个Python遍历文件脚本,对查到的文件进行特定的处理.没啥技术含量,但是也记录一下吧. 代码如下 复制代码 #!/usr/bin/python # -*- coding: utf-8 ...

  4. python提取文件_Python文件读取常用方法

    1. 关于读取文件 f.read() 读取文件中所有内容 f.readline() 读取第一行的内容 f.readlines() 读取文件里面所有内容,把每行的内容放到一个list里面 注:因为文件指 ...

  5. 如何用python创建文件_python如何创建文件

    python建立文件怎么弄 python建立文件的方法: 首先在电脑桌面上,新建一个名为a的文件夹. 并记住这个文件夹里面的绝对路径,我这是: C:\Users\Administrator\Deskt ...

  6. python编程语言继承_Python面向对象程序设计类的封装与继承用法示例

    本文实例讲述了Python面向对象程序设计类的封装与继承用法.分享给大家供大家参考,具体如下: 访问限制(封装) 1.概念 面向对象语言的三大特征:封装, 继承, 多态. 广义的封装: 类和函数的定义 ...

  7. python处理图片文件_python 学习(二)处理图片、TXT文档

    一.尝试实现场景:将一个txt文档内容读到程序的数组内 1.新建一个文档 array.txt,写入内容 ,如: 2.使用命令行读取: 1)在上述新建文档位置相同目录下打开命令提示窗 2)打开文件,读入 ...

  8. python json文件_python读写json文件的简单实现

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族 ...

  9. python npy文件_python实现npy格式文件转换为txt文件操作

    如下代码会将npy的格式数据读出,并且输出来到控制台: import numpy as np ##设置全部数据,不输出省略号 import sys np.set_printoptions(thresh ...

  10. python封装举例_Python面向对象封装操作案例详解

    本文实例讲述了Python面向对象封装操作.分享给大家供大家参考,具体如下: 目标 封装 小明爱跑步 存放家具 01. 封装 封装 是面向对象编程的一大特点 面向对象编程的 第一步 -- 将 属性 和 ...

最新文章

  1. 编写 Spring MVC 控制器的 14 个技巧
  2. c# imager让图片有圆角unity_Qt编写自定义控件24-图片轮播控件
  3. mysql 跨库复制_Mysql跨数据库(在同一IP地址中)复制表
  4. java for循环的这种写法怎么理解:for (; ; ) {},
  5. pxe+kickstart 自动化部署linux操作系统
  6. 探索比特币源码6-公钥
  7. boost asio io_context 没任务不退出
  8. AOP (面向切面编程)
  9. JavaScript:利用StringBuffer类提升+=拼接字符串效率
  10. DFS 事件id 2104 2004 报错
  11. 区间DP HDU 2476
  12. AndroidDeveloper 读者专属福利
  13. 【Android】 NDK开发中JNI配置及调用GPIO
  14. 搞科研必须了解的五十个学术网站
  15. java-net-php-python-java交通事故档案管理系统PPT计算机毕业设计程序
  16. android textview 文字倒影,textview 倒影
  17. java 视频边下边播,VideoViewDemo android 播放器,支持边下边播 238万源代码下载- www.pudn.com...
  18. 无法识别的USB设备解决方法
  19. Python调用华为API进行图像标签
  20. 华为桌面云虚拟机如何安装Ubuntu 20.04.3-live-server

热门文章

  1. springboot总结(一)
  2. 高数基础_第1节_概述以及预备知识
  3. PMP合同的类型选择
  4. 没有事业的女人会很惨
  5. java中集合的概念
  6. Excel图表—正态分布概率分布图(概率密度函数图及累积概率分布图)的绘制-Part 1
  7. 系统集成项目管理工程师06《项目成本管理》
  8. 【Algorithm】数学归纳法
  9. 网络流(Network Flow)
  10. 直线端点画垂线lisp_利用lisp给cad直线取整?