Python
的类分为经典类与新式类。Python2.7之前的版本中可以采用经典类,经典类继承父类的顺序采用深度优先算法,但在Python3之后的版本就只承认新式类了。新式类在python2.2之后的版本中都可以使用,新式类的继承顺序采用C3算法,其继承顺序可以通过查看MRO列表获取。
经典类与新式类的区别
经典类是默认没有派生自某个基类的,而新式类默认派生自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
16
class 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
16
class 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
21
class 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算法的不同搜索顺序如下:
DFS深度优先搜索(FCBAED)
BFS广度优先搜索(FCEBDA)
C3算法(FCBEDA)
对于新式类,可以用instance.__mro__或instance.mro()来查看其MRO(Method
Resolution Order 方法解析顺序)列表。对于上文代码中的类F的MRO如下:
1
2
print F.mro()
(, , , , , , )
即C3算法的解析结果。
同时为了解决多重继承中的调用父类问题,python2.2之后引入了super。