彻底掌握python类MRO(方法解析顺序)————原文章merge移除规律有点不对 - Go语言中文社区

彻底掌握python类MRO(方法解析顺序)————原文章merge移除规律有点不对


python中定义一个类允许继承自多个父类,各父类之间可能从在父子关系或兄弟关系。

兄弟关系指有共同的父类

so....

对于子类中的属性及方法到底继承自哪个父类了?

那么就让我们来掌握python中的 MRO(Method Resolution Order):方法解析顺序

 

先上结论

    1. python2.2以前的版本 经典类时代,采用DFS(深度优先搜索(子节点顺序:从左到右))

    2. python2.2新式类诞生, 经典类DFS, 新式类BFS (广度优先搜索)    类名称.__mro__可以查看MRO

                存在的问题: DFS: 只能继承无法重写  单调性        BFS:单调性

    3. python2.3开始新式类用C3算法。

    4. python3开始全部用新式类,当然就是C3算法。

 

经典类:一种没有继承的类,实例类型都是type类型,如果经典类被作为父类,子类调用父类的构造函数时会出错

 

经典类的MRO(DFS) python3及以前均存在。且均为DFS深度优先

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

# 代码1

import inspect

# 正常继承模式

class E:

    pass

class D:

    pass

class C(E):

    pass

class B(D):

    pass

class A(B, C):

    pass

print(inspect.getmro(A))

# MRO: ABDCE

 

 

# 代码2

import inspect

# 棱形继承模式

class D:

    pass

class C(D):

    pass

class B(D):

    pass

class A(B, C):

    pass

print(inspect.getmro(A))

# MRO:A B D C

 

棱形继承模式下:存在公共父类(D)的多继承,这种情况下DFS必定经过公共父类(D),假使公共父类(D)有一些属性或方法,但是子类(C)又重写了这些属性或者方法,那么按照DFS顺序必定是会先找到D的属性或方法,那么C的属性或者方法将永远访问不到。

存在的问题:棱形继承模式下出现子类只能继承无法重写的现象。 (所以新式类不会采用DFS)

 

 

python2.2新式类MRO(BFS)广度优先

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

# 代码片段3   python2.2正常继承模式

class E(object):

    pass

class D(object):

    pass

class C(E):

    pass

class B(D):

    pass

class A(B, C):

    pass

print(A.__mro__)

# MRO: ABCDE

 

 

# 代码片段4   python2.2新式类棱形继承模式

class D(object):

    pass

class C(D):

    pass

class B(D):

    pass

class A(B, C):

    pass

 

print(A.__mro__) #查看新式类的MRO

# A B C D

 

优缺点分析:

    正常继承模式,看起来正常,不过实际上感觉很别扭,比如B明明继承了D的某个属性(假设为foo),C中也实现了这个属性foo,那么BFS明明先访问B然后再去访问C,但是为什么foo这个属性会是C?这种应该先从B和B的父类开始找的顺序,我们称之为单调性。

第二种,棱形继承模式,这种模式下面,BFS的查找顺序虽然解了DFS顺序下面的棱形问题,但是它也是违背了查找的单调性。

存在的问题:虽然解决DFS棱形模式的只继承不能复写的问题,但引入了不符合单调性的问题。

因为违背了单调性,所以BFS方法只在Python2.2中出现了,在其后版本中用C3算法取代了BFS。

 

 

python2.3及以后新式类MRO(C3算法)

C3算法解决了单调性问题和只能继承无法重写问题,上面代码段中的例子在python2.3及以后执行,MRO如下

 

C3算法详解:

L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)
So in our case L[F(A, B)] = F + merge(L[A], L[B], A, B)

 

一个列表【C1, C2......CN】的头及尾计算:

head(C1C2⋯CN)=C1head(C1C2⋯CN)=C1tail(C1C2⋯CN)=C2C3⋯CN

C3线性化步骤:

1. 选取merge中的第一个列表记为当前列表 K。

2. 从列表K中拿出第一个元素,如果该元素在其他列表的tail(或者不出现)中则将该元素移入类C的线性化列表中。将其从merge的每一个列表中移出。

3. 否则设置K为merge中的下一个列表,重复步骤2.

4.如果 merge 中所有的类都被移除,则输出类创建成功;如果不能找到下一个 h,则输出拒绝创建类 C并抛出异常。

 

通过C3线性化分析以上两种模式的MRO.

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

L[B]=L[B(D)]=B+D

L[C]=L[C(E)]=C+E

L[A(B,C)] = A+ merger(B+D,C+E,B,C)

 

最终MRO列表为L[A]

    1. L[A]=【A】+ merger(B+D,C+E,B,C)

    2.  判断B+D  第一个元素为B 在 其他列表  C+E   B    C中都不不在tail(X)中, 所以加入B   L[A]=[A,B]+ merger(D,C+E,C)

    3. merger(D,C+E,C), 拿出D,   L[A]=[A,B,D]+ merger(C+E,C)

    4.  merger(C+E,C) 拿出C, L[A]=[A,B,D,C]+ merger(E)

    5. L[A]=[A,B,D,C,E]

 

分析下图中的MRO

 

L[E] = [E,object]

L[D] = [D,object]

L[F] ] [F,object]

 

L[B] = B +merge(L[E],L[D],E,D)

L[B] = B+merge([E,object],[D,object】,E,D)

L[B] = [B,E,D,object]

 

L[C] = [C,D,F,object]
 

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

L[A] = A+merge([B,E,D,object],[C,D,F,object], B, C)

 

L[A]= 【A,B】+ merge([E,D,object],[C,D,F,object], C)

L[A]= 【A,B,E】+ merge([D,object],[C,D,F,object], C)           

L[A]= 【A,B,E,C】+ merge([D,object],[D,F,object])     # 注意从上一行到这一行,先选择D但不符合条件,然后选择C

# L[A]= 【A,B,E,C】+ merge([D,object],[D,F,object]) #注意这里,当两个D都在首位置的时候,也移除D

#L[A]= 【A,B,E,C】+ merge([D,object],[D,F,object],[G,object]) # 如果是这样移除F,G,然后是

L[A]= 【A,B,E,C,D】+ merge([object],[F,object])

L[A]= 【A,B,E,C,D,F】+ merge([object],[object])    #注意从上一行到这一行,没选择object

L[A]= 【A,B,E,C,D,F,object】

 

所以类A的MRO 为 【A,B,E,C,D,F,object】 

掌握MRO了吗?

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Joohn2015/article/details/85097196
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-07 18:49:10
  • 阅读 ( 1147 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢