Python支持多继承,与C++一样都会出现一种问题:子类继承的多个父类又继承了同一个父类,这时就有可能会出现父类构造方法被调用多次的情况。关于这个问题,我找了一些资料,虽然没有亲自全部验证,这里我总结一下自己对这个问题的看法。
Python和C++的关于这个问题的解决方案不太一样,当然Python还要看它的版本。
C++用的方案是引入了虚继承的语法避免同一个类被构造了多次。
Python用的方法是MRO(method resolution order,方法解析顺序) 。在在Python2.3之前,MRO的实现是基于DFS的,而在Python2.3以后MRO的实现是基于C3算法。找到的资料解释了一下更换算法的原因:
为什么采用C3算法
C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。
本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。
------------------------------新式类和旧式类中查找属性的顺序不同--------------------------
在新式类中,查找一个要调用的函数或者属性的时候,是广度优先搜搜的。
在旧式类当中,是深度优先搜索的。如下图所示:
来一个例子:
# -*- coding:utf-8 -*-
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()
例子中定义D类的时候,D是新式类,所以D的所有子类都是新式类。
A的实例对象f在调用foo函数的时候,根据广度优先搜索原则,调用的是C类里面的foo函数。
上面的代码输出class C
如果定义D类的时候直接class D,而不是class D(object),那么上述代码就该输出class D了。
如果我用的是super来解决多继承的初始化问题的话,那么到底是怎么初始化的呢?
这是我一开始想到的一个问题:
class A(object):
def __init__(self,a):
print a
class B(object):
def __init__(self,a,b):
print a+b
class C(A,B):
def __init__(self):
super(C,self).__init__(?)
obj = C()
第十一行的那个'?'的位置到底是填是什么进去才对呢?
我一开始认为因为是多继承多以需要初始化父类的时候A,B都需要初始化,那么问题来了,super(C,self).init(?)怎么写?
我试了一下:
① ? = 1 --> 输出1
② ? = 1,2 --> TypeError: init() takes exactly 2 arguments (3 given)
定义C的时候改成class(B,A)
③ ? = 1 --> TypeError: init() takes exactly 3 arguments (2 given)
④ ? =1,2 --> 输出3
再来看另一段代码:
class A(object):
def func(self):
print 1
class B(object):
def __init__(self):
print 2
class C(A,B):
def __init__(self):
super(C,self).__init__()
obj = C()
#The output is 3
说明什么问题?在调用super(classname,self).init()的时候应该调用在继承的父类列表里面有实现init()这个方法而且最靠左边的那个父类的构造方法,而且这个init(?)的'?'一定要与父类列表的里面第一个有构造方法的父类的构造方法签名一样才可以。
所以如果需要对所有父类都进行一遍初始化,还是使用类通过类名调用未绑定的初始化方法好(我说这一句话是因为我还不是很了解super)。
说一下super的使用:super( classname,对象(一般情况是self) ) 返回的是一个super对象,你可以把它当作父类列表里面有实现init()这个方法而且最靠左边的那个父类的一个对象就可以了。
参考文章地址:
http://blog.csdn.net/imzoer/article/details/8737642
http://blog.csdn.net/zyflying/article/details/8636006