Python学习笔记:四

多重继承

Python可实现多重继承

1
2
3
4
class Student(A,B)
def __init__(self, a, b, c):
A.__init__(a)
B.__init__(b)

存在的问题:
多重继承时如果两个父类存在同名方法如何解决:

Python的类分为经典类 和 新式类。
经典类是python2.2之前的东西,但是在2.7还在兼容,但是在3之后的版本就只承认新式类了。
新式类在python2.2之后的版本中都可以使用。
经典类和新式类的区别在于:

  • 经典类是默认没有派生自某个基类的,而新式类是默认派生自object这个基类的
  • 经典类在类多重继承的时候是采用从左到右深度优先原则匹配方法的.而新式类是采用C3算法(不同于广度优先)进行匹配的
  • 经典类是没有__MRO__和instance.mro()调用的,而新式类是有的。

另外,现在是推荐采用新式类来代替经典类。因为经典类对于多重继承采用的从左到右深度优先匹配算法存在一些问题。也就是如果方法同名,有的时候会绕过一些想要访问的方法,只指向一个方法。

首先,先复习一下数据结构:
深度优先算法,广度优先算法,还有Python采用的C3算法

深度优先算法
可从某个定点v出发,访问此顶点,然后一次从v的违背访问的邻接点触发深度优先遍历图,直至途中所有和v有路径相同的顶点都被访问到;若此时途中尚有顶点未被访问,则另选途中一个未曾被访问的顶点做开始点。

通俗一点,就是从顶点开始遍历,从左到右。例如,先选顶点v,顶点v存在左右两个子节点v1, v2,就先选则左边的v1节点。这个时候就又可以把v1当成一个顶点来判断了,判断它是否有左右两个点,原理同上。等左边的节点v1包含它的所有子节点都被遍历完了之后,再开始右边节点v2的遍历,此时又把v2当成一个顶点。
这里写图片描述
图画得略微有点丑,将就看看。: )
这张图用深度优先算法来遍历就是: v1, v2, v4, v5, v3, v6
如果v3还存在一个左节点v7,那结果就是 v1, v2, v4, v5, v3, v7, v6

广度优先算法
通俗理解,就是按树的层次从左到右依次遍历。这一层遍历完了之后就开始遍历下一层,每一层都从左到右遍历
这里写图片描述
还是这张丑图 : )
用广度优先算法就是: v1, v2,v3,v4, v6, v5。一层层的来,从左到右

C3算法
有点类似与广度优先算法,但是存在一些区别。
例如,如果是上面的那张图的话,结果和广度优先是一样的(只是结果一样,算法原理不同)。但是由于算法存在一些区别,例如下一个例子就可以看出它们的区别了。
 这里写图片描述
这图大致的意思是: A有两个父类B和C,B类又有一个父类D,D类的父类是O,而父类C的直接父类是O,少掉了D类这一层。
这样的话用C3算法遍历出来的结果就是ABDCO,而广度优先算法则是ABCDO。

这里就要介绍一下C3算法的核心merge了。

取第一个列表的头,也就是L[B,object] ,如果这个头不在任何表的尾部,那么将它加到Class D的线性化中,并且从合并中的列表里删除 ;否则查找下一个列表的头,如果是个好的表头则取出它。 需要注意的是: 表头指是第一个元素 ,尾部是指除表头之外的其它所有元素 。如[A,B,C,D,E,F],A是表头,[B,C,D,E,F]是尾部。

这段话取自《python高级编程》
用一个更简单的例子来表示此算法。
这里写图片描述

算法表达式:
L(D(B,C)) = D + merge( [B,object] ,[C,object] , [B,C] )
= [D, B] + merge( [object], [C,object] , [C] )
= [D, B,C] + merge( [object] , [object] )
= [D, B,C,object]

注解:

  • 第一行中列表[B,object]的表头是B,没有出现在其它表([C,object] 、[B,C] )的尾部。因此取出B并与D结合, 删除其余B
  • 第二行中列表[C,object]的表头是C,没有出现在其它表([object] 、[C] )的尾部 ,注意 [C] 这个列表只有表头,没有尾部。同样的,取出C与[D,B]结合,删除其余C

    因此,这个图遍历的结果就是 D,B,C,A,Object

多重继承时同名方法的冲突问题

就上面的图来说,如果采用经典类,也就是使用深度优先算法,依次遍历的结果为 DBAC, 这样的话就存在一个问题。如果C重写了A当中的一个方法,那么只会调用A当中的方法而不会调用C的。
而使用新式类,也就是C3算法,遍历结果为DBCA。这样可以保证相邻父类的方法能够被优先遍历,如果不存在,才会调用更上一层的父类方法。

因此,新式类在这里首先就解决了一个优先顺序的问题,因为C很有可能重写了A的方法,而我们很多时候需要的就是C的方法。
第二个解决的问题就是单调性的问题。保证在不断的继承中同样能够保持顺序

总结

这个C3算法是现在Python中已经封装好了的算法,当遇到多重继承的时候会自动选择该算法,这里总结归纳它只是为了了解它里面的层次与结构。