本文主要作科普用,在真实编程中不建议使用多重继承,或者少用多重继承,避免使代码难以理解。python
关于多重继承,比较重要的是它的方法解析顺序(能够理解为类的搜索顺序),即MRO。这个跟类是新式类仍是经典类有关,由于二者的搜索算法不一样。算法
在Python2及之前的版本,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于新式类;反之,不禁任意内置类型派生出的类,则称之为经典类编程
在Python3之后,没有该区分,全部的类都派生自内置类型object,无论有没有显式继承object,都属于新式类。spa
对于经典类,多重继承的MRO是深度优先,即从下往上搜索;新式类的MRO是采用C3算法(不一样状况下,可表现为广度优先,也可表现为深度优先)。code
C3算法表现为深度优先的例子:cdn
# C3-深度优先(D -> B -> A -> C)
class A:
var = 'A var'
class B(A):
pass
class C:
var = 'C var'
class D(B, C):
pass
if __name__ == '__main__':
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>]
print(D.mro())
# A var
print(D.var)
复制代码
C3算法表现为广度优先的例子:blog
# C3-广度优先(D -> B -> C -> A)
class A:
var = 'A var'
class B(A):
pass
class C(A):
var = 'C var'
class D(B, C):
pass
if __name__ == '__main__':
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
print(D.mro())
# C var
print(D.var)
复制代码
注:关于C3的详细算法本文不讨论,由于我也搞不懂(狗头保命)继承
其实菱形多重继承上面已经有例子了,就是C3算法表现为广度优先
这个例子,画起图来是这样的: ip
菱形多重继承会致使的一个问题是A初始化了两次,以下:get
class A:
def say(self):
print("A say")
class B(A):
def say(self):
print("B say")
A.say(self)
class C(A):
def say(self):
print("C say")
A.say(self)
class D(B, C):
def say(self):
print("D say")
B.say(self)
C.say(self)
if __name__ == '__main__':
dd = D()
dd.say()
复制代码
若是只想调用一次A,可以使用super方法:
class A:
def say(self):
print("A say")
class B(A):
def say(self):
print("B say")
super().say()
class C(A):
def say(self):
print("C say")
super().say()
class D(B, C):
def say(self):
print("D say")
super().say()
if __name__ == '__main__':
print(D.mro())
dd = D()
dd.say()
复制代码
1.若是父类有init方法,子类没有,则子类默认继承父类的init方法
class A:
def __init__(self, a1, a2):
self.a1 = a1
self.a2 = a2
def say(self):
print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
class B(A):
def say(self):
print("B say, a1: %s, a2: %s" % (self.a1, self.a2))
if __name__ == '__main__':
# 由于B继承了A的init方法,因此也要传入 a1,a2参数
bb = B("10", "100")
bb.say()
复制代码
2.若是父类有init方法,子类也有,可理解为子类重写了父类的init方法
class A:
def __init__(self, a1, a2):
self.a1 = a1
self.a2 = a2
def say(self):
print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
class B(A):
def __init__(self, b1):
self.b1 = b1
def say(self):
print("B say, b1: %s" % self.b1)
if __name__ == '__main__':
# 此处B重写了A的init方法,因此只须要传入b1参数,也没有拥有a1,a2属性
bb = B("10")
bb.say()
复制代码
3.对于第二点,为了能使用或者扩展父类的行为,更常见的作法是在重写init方法的同时,显示调用父类的init方法(意味传的参数要变成3个)。
# 三种写法
class A:
def __init__(self, a1, a2):
self.a1 = a1
self.a2 = a2
def say(self):
print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
class B(A):
def __init__(self, b1, a1, a2):
# 第一种写法: Python2的写法
# super(B, self).__init__(a1, a2)
# 第二种写法(推荐):Python3的写法,与第一种等价
super().__init__(a1, a2)
# 第三种写法:与前两种等价,不过这种须要显示调用基类,而第二种不用
# A.__init__(self, a1, a2)
self.b1 = b1
def say(self):
print("B say, a1: %s, a2: %s, b1: %s" % (self.a1, self.a2, self.b1))
if __name__ == '__main__':
# 此处B重写了A的init方法,因此只须要传入b1参数,也没有拥有a1,a2属性
bb = B("10", "100", "1000")
bb.say()
复制代码
注意 __init__
方法不要写错,避免写成__ini__
或者其余的,由于两个是在太像,出了问题很难排查(坑过两次)。