分界

python的MRO算法有新旧两种,但并不是以python2和python3为界,具体的分隔为:在python2中如果定义类的时候没有指定父类是object,即定义为

class A:

pass

的使用旧算法。如果指定父类为object的:

class A(object):

pass

使用新算法,python3都是用新算法。

旧算法

先看示例,类图:

python_a.png

代码:

class A:

def who_am_i(self):

print("I am a A")

class B(A):

def who_am_i(self):

print("I am a B")

class C(A):

def who_am_i(self):

print("I am a C")

class D(B,C):

def who_am_i(self):

print("I am a D")

d1 = D()

d1.who_am_i()

毫无疑问这段代码会输出“I am a D”,稍微改一下D的定义:

class D(B,C):

pass

这时候找到了B实现的方法所以打印出“I am a B”,如果我们把B的实现也去掉:

class B(A):

pass

奇怪的事情发生了,输出是“I am a A”,因为旧算法的MRO推导算法很直观,它使用树状结构组织类。当函数调来到来的时候从左往右的使用深度优先遍历父类:

1,检查对象是否实现了方法

2,如果没有找到,检查首个父类是否实现了该方法

3,如果没有找到,检查本类是否还继承了其他类,是的话从其他分支向上追溯

所以对于本例,查找的顺序就是D B A C A,最后一个A不重复加入最终的顺序是D B C A

来一个例子检查:

class A1():

# def who_am_i(self):

# print("I am a A1")

pass

class A2():

def who_am_i(self):

print("I am a A2")

class A3():

def who_am_i(self):

print("I am a A3")

class B(A1, A2):

# def who_am_i(self):

# print("I am a B")

pass

class C(A3):

def who_am_i(self):

print("I am a C")

class D(B,C):

# def who_am_i(self):

# print("I am a D")

pass

d1 = D()

d1.who_am_i()

类图为:

python2_mro.png

套用刚才的规则,查找方法的顺序应该是D B A1 A2 C A3,所以上述代码在python2上的输出是“I am a A2”

新算法

仍然以第一段代码作为例子,重新贴一下类图

python_a.png

新算法在构建MRO的时候第一步与旧算法一样,所以初步结果是D B A C A,然后从左到右判断每个元素是否为“好头(good head)”,不是的话从链中去掉。“好头(good head)”的定义是 - 从该元素开始到链尾,不存在任何元素是该元素的子类 - 初步结果的D, B没问题,第一个A不是好头因为C是其子类所以去掉,再往后的C A也没问题了,最终结果是D B C A,所以在python 3以及当A继承于object的时候如果D,B都不实现who_am_i则最终输出的是“I am a C”。

不可构建的MRO - 新算法补充

考虑如下蛋疼的继承结构:

Use case.png

代码:

class X():

def who_am_i(self):

print("I am a X")

class Y():

def who_am_i(self):

print("I am a Y")

class A(X, Y):

def who_am_i(self):

print("I am a A")

class B(Y, X):

def who_am_i(self):

print("I am a B")

class F (A, B):

def who_am_i(self):

print("I am a F")

f = F()

f.who_am_i()

如果使用“新算法”,按照上述章节的分析,构建出来的MRO结果应该是F A B Y X,使用python 3跑一遍,结果得到输出:

Traceback (most recent call last):

File "bad_mro.py", line 26, in

class F(A, B):

TypeError: Cannot create a consistent method resolution

order (MRO) for bases X, Y

这又是啥毛病呢,实际上新算法用的是线性算法记为L[]运算以及merge()操作来构造MRO,改线性算法描述为:类C的线性结果是C与merge(C的所有父类的线性结果)之和,用表达式表示为:

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

上边例子的表达式就是:

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

merge()运算的规则是:

1,取出merge()中第一个list的第一个元素作为head

2,如果这个head不出现在其他list的尾部,则它是一个“好头”,可以从merge()中移出来

3,否则,取出下一个list中的第一个元素作同样的判断

4,重复上述过程直至所有的类都移到merge()算子的外部则构建MRO完成

5,否则,改继承结果无法生成MRO

所以对于L[F(A, B)] = F + merge(L[A], L[B], A, B)这个表达式,L[A], L[B]可以先按照L[]算法展开为:

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

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

例如这里的merge(X,Y,X,Y)首先判断X,因为X不在其他list的尾部(X算是list的头部)所以先提取X出来,这时候变成了A,X+merge(Y,Y)显然Y也能直接提取出来了;merge(Y,X,Y,X)同理。

把这俩结果代回去,总式子为:

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

这时候先判断A,因为不在其他list的尾部,所以提出出来式子变为:

F,A + merge((X, Y), (B, Y, X), B)

再判断第二个list的第一个元素B,也不在其他list的尾部,所以提出来:

F,A,B + merge((X, Y), (Y, X))

接着第二轮判断第一个list的头元素X,结果在第二个list中发现了X在尾部不能提取;再判断Y*结果发现在第一个list的尾部也不行。所以最终就遇到了不可构建MRO的情况!

结论

可见,虽然python的多重继承非常强大但是必须小心注意继承顺序(避免上述章节中提及的MRO不可构建的继承结构),与Java这种单继承语言不同 - super()总是沿着继承链向上追溯。python中方法的super()并不总是指向其声明的父类的同名方法,而是沿着MRO从左到往的传播。

python多继承顺序_Python多重继承方法解析顺序(MRO构建算法)相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. Python3的方法解析顺序(MRO)

    Python 2.3 的新式类的 C3 算法.它也是 Python 3 唯一支持的方式(笔者使用python3,所以就先讲这种的) 一个例子: class D(object): pass class ...

最新文章

  1. 操作系统学习:Linux0.12文件异步IO
  2. 后处理程序文件大小的变量_【每日一题】(17题)面试官问:JS中事件流,事件处理程序,事件对象的理解?...
  3. js不停地触发按钮的事件
  4. matlab regress()
  5. [Oracle] oracle统计信息
  6. [JavaWeb-JavaScript]JavaScript注释数据类型
  7. QS首发大学百强排名,华东五校表现惊艳,老牌985望尘莫及
  8. word2vec数学原理详解
  9. 理正深基坑弹性计算方法_理正深基坑整体计算与单元计算的区别
  10. python地理数据处理环境搭建
  11. 高分影像批处理第一回——数据格式分析与整理
  12. 广告联盟反作弊一些常识
  13. 埃森哲互动成为全球最大的数字营销服务商
  14. 程序猿的一些幽默趣闻 个个经典
  15. 群辉安装python3,pip,环境变量配置
  16. 利用阿里云搭建NFS服务器
  17. html鼹鼠游戏,疯狂鼹鼠
  18. Golang知识点二、GMP调度模型
  19. Nacos服务注册与发现源码(一)之gRPC协议的实例注册
  20. 电子邮件服务器需要那两个协议,电子邮件有哪两个协议

热门文章

  1. 某程序员哀叹:连续帮三任女朋友进360,京东等互联网大厂,进去后却都惨遭分手!...
  2. 因4元而市值蒸发400亿!美团“大数据杀熟”翻车!回顾2020年互联网大事件!...
  3. 推荐一位高性能服务开发专家!是他让我从专科,蜕变为年薪百万后端开发!...
  4. 从 LRU Cache 带你看面试的本质
  5. 分享10个实用的高效办公神器,极大地提高办公效率
  6. B2B2C网站系统建设的常见误区
  7. JSP内置对象-request
  8. ubuntu mysql 内存满了_ubuntu – 如何为mySQL分配内存限制?
  9. 西文是指什么_中西文化的关键性差别
  10. RFID读写器Impinj R420开发C#