ITPub博客

首页 > 数据库 > 数据库开发技术 > python面向对象编程篇(9):多继承中MRO再次思考

python面向对象编程篇(9):多继承中MRO再次思考

原创 数据库开发技术 作者:程序员小钟同学 时间:2021-03-16 18:00:38 0 删除 编辑

本文相关描述主要是个人的认知和见解,如有不当之处,还望各位大佬指正。

个人其他博客地址:

简书:https://www.jianshu.com/u/d6960089b087

掘金:https://juejin.cn/user/2963939079225608

小钟同学 | 文 【原创】| QQ:308711822

1 python中的多继承问题中的方法解析顺序MRO(Method Resolution Order)和C3算法思考

我们知道在python在类根据不同的版本有不同的定义且对应的使用的算法不同:

  • 一:经典类(深度优先搜索)
  • 二:新式类(广度优先搜索)
  • 三:新式类(C3算法实现:Python3中全部都是新式类,不需要object继承)

多继承中引发的MRO在旧版本里使用的深度优先算法,这里暂时不去探究。

2 什么是MRO?

MRO是出现在面向对象变成语言中关于继承的对象中一个查询相关属性和方法的定位搜索算法。

  • 它代表了类继承的顺序.
  • 是一个有序列表L,在类被创建时就计算出来。

大白话的意思就是:我继承多个父类的时候,调用相关的方法的是,如何快速知道我要调用的父类的那个方法(或者我自身的方法)。

3 MRO主要是为了解决什么问题?

多继承情况下,各自的父类的属性和方法都各自为政。因此也出现所谓一个【菱形继承】的问题。

如图示:

在【菱形继承】图示里,子类进行相关的方法调用的时候,如果确定是调用哪个父类的方法的时候就需要一个调度算法去搜索定位,所以这个搜索的顺序就是MRO。

4 C3算法是啥?

在解决【菱形继承】的问题里,我们判断mro需要先确定一个线性序列,然后查找路径由由序列中类的顺序决定。在python3中,使用了C3算法,C3算法就是生成一个线性序列。

Python提供了__mor__和mor() 来查看类的方法解析顺序

4 C3算法示例图解:

首先我们的【菱形继承】图示如:

转为我们代码为:

class A1: pass
class A2: pass
class A3: pass
class B1(A1,A2): pass
class B2(A2): pass
class B3(A2,A3): pass
class C1(B1): pass
class C2(B1,B2): pass
class C3(B2,B3): pass
class D(C1, C2, C3): pass

代码解说:

  1. A1 A2 A3的基类分别是object(新式类可以不用明写)
  2. B1继承-A1 A2
  3. B2继承-A2
  4. B3继承-A2 A3
  5. C1继承-B1
  6. C2继承-B1 B2
  7. C2继承-B2 B3
  8. D继承-C1 C2 C3

我们查看D的MRO 顺序查找流程:

for s in D.__mro__:
    print(s)

输出的结果为:

<class '__main__.D'>
<class '__main__.C1'>
<class '__main__.C2'>
<class '__main__.B1'>
<class '__main__.A1'>
<class '__main__.C3'>
<class '__main__.B2'>
<class '__main__.B3'>
<class '__main__.A2'>
<class '__main__.A3'>
<class 'object'>

从左到右的分析

左右对比

查找规律流程:

文字说明:

先明确几个查询点:

  • 当前类
  • 最底层查找类
  • 底层未查询的父类列表
  • 预查询父类
  • 预查询父类的基类是否是object
  • 与查询的父类是否还以子类,且同时又是最底层查找类的父类
  • 取出预查询父类,加入到连表中(作为下一轮的当前类)

class A1: pass
class A2: pass
class A3: pass
class B1(A1,A2): pass
class B2(A2): pass
class B3(A2,A3): pass
class C1(B1): pass
class C2(B1,B2): pass
class C3(B2,B3): pass
class D(C1, C2, C3): pass
print("从D开始查找:")
for i,s in enumerate( D.__mro__):
    print("第"+str(i+1)+"轮:",s)


输出结果Wie:

从D开始查找:
第1轮: <class '__main__.D'>
第2轮: <class '__main__.C1'>
第3轮: <class '__main__.C2'>
第4轮: <class '__main__.B1'>
第5轮: <class '__main__.A1'>
第6轮: <class '__main__.C3'>
第7轮: <class '__main__.B2'>
第8轮: <class '__main__.B3'>
第9轮: <class '__main__.A2'>
第10轮: <class '__main__.A3'>
第11轮: <class 'object'>

首先我们的【菱形继承】图示如:

从左到右的分析


首先第1轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类) 预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
D D C1,C2,C3 C1 否 有,C1 C1 D,C1

首先第2轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类) 预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
C1 D C2,C3 B1 否 B1还存在C2子类,删除C2(取出) C2 D,C1,C2

首先第3轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类)预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
C2 D C3 B1 否 否,B1除了(C1,C2没其他子类咯) B1 D,C1,C2,B1

PS:

  • 预查询父类是否是object
  • 预查询父类是否还有子类,且同时又是最底层查找类的父类
  • ==> 都是 否的情况下,则把预查询父类作为的取出的类 首先第4轮:
当前类 最底层查找类 底层未查询的父类列表 (当前类父类)预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
B1 D C3 A1 否 否,A1没有子类 A1 D,C1,C2,B1,A1

PS:

  • 预查询父类是否是object
  • 预查询父类是否还有子类,且同时又是最底层查找类的父类
  • ==> 都是 否的情况下,则把预查询父类作为的取出的类

首先第5轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类) 预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
A1 D C3 object 是,但底层D还有父类C3没有删除,(没有取出的子类), 否,object没有子类 C3 D,C1,C2,B1,A1,C3

首先第6轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类) 预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
C3 D 无 B2 否 否,B2,无子类了(C3已经取出) B2 D,C1,C2,B1,A1,C3

PS:

  • 预查询父类是否是object
  • 预查询父类是否还有子类,且同时又是最底层查找类的父类
  • ==> 都是 否的情况下,则把预查询父类作为的取出的类

首先第7轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类) 预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
B2 D 无 A2 否 有,B3 B3 D,C1,C2,B1,A1,C3,B2

首先第8轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类) 预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
B3 D 无 A2 否 否 A2 D,C1,C2,B1,A1,C3,B2,A2

PS:

  • 预查询父类是否是object
  • 预查询父类是否还有子类,且同时又是最底层查找类的父类
  • ==> 都是 否 的情况下,则把预查询父类作为的取出的类

首先第9轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类) 预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
A2 D 无 object 是(最底层D已经没有父类未取出了) 是,A3 A3 D,C1,C2,B1,A1,C3,B2,A2,A3

PS:

  • 预查询父类是否是object
  • 预查询父类是否还有子类,且同时又是最底层查找类的父类
  • ==> 都是 是 的情况下,则把 预查询父类类剩余的子类作为取出的类

首先第10轮:

当前类 最底层查找类 底层未查询的父类列表 (当前类父类) 预查询父类 预查询父类是否是object 预查询父类是否还有子类,且同时又是最底层查找类的父类 取出当前类/剩余子类,加入连表中类(下一轮的当前类) 当前MRO链表
A3 D 无 object 是 无 object D,C1,C2,B1,A1,C3,B2,A2,A3,object

参考资料:

https://blog.csdn.net/u011467553/article/details/81437780


原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-02-20

  • 博文量
    224
  • 访问量
    111698