Python 类的多重继承
Python 的类分为经典类与新式类。Python2.7之前的版本中可以采用经典类,经典类继承父类的顺序采用深度优先算法,但在Python3之后的版本就只承认新式类了。新式类在python2.2之后的版本中都可以使用,新式类的继承顺序采用C3算法,其继承顺序可以通过查看MRO列表获取。
Table of Contents
经典类与新式类的区别
经典类是默认没有派生自某个基类的,而新式类默认派生自
object
基类:1
2
3
4
5
6
7# old style
class A():
pass
# new style
class A(object):
pass经典类在类多重继承的时候是采用的从左至右深度优先的匹配方法,而新式类采用C3算法(不同于广度优先)进行匹配的。
经典类没有
__MRO__
和instance.mro()
调用,而新式类有。
经典类中的继承问题
经典类中采用深度优先的匹配方法,可能导致在查询继承树中绕过后面的父类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class D():
def foo(self):
print "class D"
class B(D):
pass
class C(D):
def foo(self):
print "class C"
class A(B, C):
pass
f = A()
f.foo()
其输出为:class D。
新式类采用C3算法(区别于广度优先的原则)进行搜索,若使用新式类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class D(object):
def foo(self):
print "class D"
class B(D):
pass
class C(D):
def foo(self):
print "class C"
class A(B, C):
pass
f = A()
f.foo()
输出为:class C 搜索的顺序如下图所示:
C3算法
C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。
- 本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
- 单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。
深度优先搜索用栈(stack)来实现,整个过程可以想象成一个倒立的树形:
- 把根节点压入栈中。
- 每次从栈中弹出一个元素,搜索所有在它下一级的元素,把这些元素压入栈中。并把这个元素记为它下一级元素的前驱。
- 找到所要找的元素时结束程序。
- 如果遍历整个树还没有找到,结束程序。
广度优先搜索使用队列(queue)来实现,整个过程也可以看做一个倒立的树形:
- 把根节点放到队列的末尾。
- 每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。
- 找到所要找的元素时结束程序。
- 如果遍历整个树还没有找到,结束程序。
对于同一段程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class A(object):
def foo(self):
print "A"
class B(A):
def foo(self):
print "B"
class C(B):
def foo(self):
print "C"
class D(A):
def foo(self):
print "D"
class E(D):
def foo(self):
print "E"
class F(C, E):
def foo(self):
print "F"
f = F()
f.foo1()
当使用深度优先搜索,广度优先搜索及C3算法的不同搜索顺序如下:
对于新式类,可以用instance.__mro__
或instance.mro()
来查看其MRO(Method Resolution Order 方法解析顺序)列表。对于上文代码中的类F
的MRO如下:
1
2print F.mro()
(<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class '__main__.A'>, <type 'object'>)
即C3算法的解析结果。
同时为了解决多重继承中的调用父类问题,python2.2之后引入了super
。
参考资料: