目录
一、菱形继承与多继承
Python在继承方面与Java不同,Python可以进行多继承,而Java则不能多继承,替代的是使用的多接口方法。Python继承问题中,最经典的一个问题是菱形继承,然而菱形继承是多继承中的一个特殊的例子。从下图中可以看到他们之间的差异和联系。后面将使用更加普遍的多继承来说明这些继承会遇到的特殊情况。
二、多继承可能面临的问题
从前面的介绍可以看到Python多继承存在一些问题如下:
- 在Class C1中调用的方法,在Class B1和Class B2有同样的实现该如何选择。
- 多继承的继承顺序问题。
- 继承过程中有super和类名继承这两者有什么问题。
- 继承过程中初始化的参数该如何申明
- 继承过程中想通过初始化类进行参数传递如何实现。
- ...
为了针对上面出现的若干问题,将对继承进行一些实验来说明上述的问题,对于方法操作介绍的很多,这里对属性的操作进行描述。
三、多继承实现的方法
在Python中多继承可以通过两种方法实现,第一个是类名继承,第二个是super,具体如下:
3.1 类名继承
类名继承的简单使用如下,注意:类名继承的过程中会初始化两次父类,这个问题可以通过super解决。
class A1:
def __init__(self, a1):
self.a1 = a1
self.a_self = 8
print(a1)
class B1(A1):
def __init__(self, b1, *args):
# A1.__init__(self, *args) # super继承全参数传递
A1.__init__(self, b1)
self.b_self = 5
self.a_self = 9
print(b1)
class C1(A1):
def __init__(self, c1, *args):
# A1.__init__(self, *args) # super继承全参数传递
A1.__init__(self, c1)
self.c_self = 7
self.a_self = 10
print(c1)
class D1(B1, C1):
def __init__(self, d1, d2):
C1.__init__(self, d1)
B1.__init__(self, d2)
# C1.__init__(self, d1)
# super(D1, self).__init__(d1, d2) # super继承
def show(self):
print(self.a_self)
print(self.b_self)
print(self.c_self)
d = D1("1", "2")
# 使用super继承的结果是1,2
# 使用类名继承的结果是1 1 2 2 A1被初始化了两次
# d.show()
print(D1.__mro__)
3.2 super继承
super继承的简单使用如下,值得注意的是在super下的多继承需要使用*args进行参数传递保证初始化不会出现问题,并且可以解决类名继承初始化两次的问题。
class A:
def __init__(self, a):
self.a = a
self.b = 3
print(a, "A")
def show(self):
print(self.a)
print("A show()")
class B(A):
def __init__(self, b1, b2, *args):
print(b1, b2, "B")
super(B, self).__init__(*args)
self.b2 = b2
self.b1 = b1
self.a = 999
def show(self):
print(self.b2, self.b1, self.a)
print("B show()")
class C(A):
def __init__(self, c1, c2, *args):
print(c1, c2, "C")
super(C, self).__init__(*args)
self.c2 = c2
self.c1 = c1
self.a = 9991
def show(self):
print("C show()")
class D(C, B):
def __init__(self, d1, d2, d3, d4, d5):
super(D, self).__init__(d1, d2, d3, d4, d5)
print(d1)
print(d2)
self.d1 = d1
self.d2 = d2
d = D(2, 3, 4, 5, 6)
print(d)
print(d.a)
d.show()
print(D.__mro__)
# 输出结果
"""
2 3 C
4 5 B
6 A
2
3
<__main__.D object at 0x0000025B243C09C8>
9991
C show()
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
"""
3.3 super继承和类名继承的性质
super继承的性质:
- 按照从左到右的顺序继承,先查询自己有没有方法,没有方法按照从左到右的顺序从父类中查询,最后再去祖类查找。
- 对于类属性而言,同样的道理,但是属性在过程中可以进行操作,例如在A的中一个属性为10,在B中和C中分别加100,200,在传给D时就变成了310。
对于第一点比较好理解,方法就是有就从自己往上搜索。在属性上有一个点,就是我在A类申明了一个变量,通过类的不断初始化,初始化(B,C)我可以将这个变量传递给D。这个时候就会出现一个情况,执行的结果是从祖类一步步递归下来。参考如下:
class A:
def __init__(self, a1):
self.a = a1
self.self_a = 10
print("A")
def show(self):
print(self, "A show()")
class B(A):
def __init__(self, b1, b2, *args):
super(B, self).__init__(*args)
self.b1 = b1
self.b2 = b2
print("B", self.self_a) # 1210
self.self_a += 100
print("B")
def show(self):
print(self, "B show()")
class C(A):
def __init__(self, c1, c2, *args):
super(C, self).__init__(*args)
self.c1 = c1
self.c2 = c2
print("C", self.self_a) # 210
self.self_a += 1000
print("C")
def show(self):
print(self, "C show()")
class E(A):
def __init__(self, *args):
super(E, self).__init__(*args)
self.self_a += 200
print(self.show()) # none,因为没有初始化,不会执行到这个方法
def show(self):
print("E show()")
class D(B, C, E):
def __init__(self, d1, d2, *args):
super(D, self).__init__(*args)
self.d1 = d1
self.d2 = d2
print("D")
# def show(self):
# print(self)
# print("D show()")
d = D(1, 2, 3, 4, 5, 6, 7)
d.show()
print(d.self_a)
print(D.__mro__)
"""
A
<__main__.D object at 0x000001FEC09B0BA8> B show()
None
C 210
C
B 1210
B
D
<__main__.D object at 0x000001FEC09B0BA8> B show()
1310
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.A'>, <class 'object'>)
"""
对于上述的结果,有人可能会疑惑:不是先初始化B,在初始化C,所以B的结果是10。这是由于B,C中都没有这个变量,因此它会一直按顺序找,再去祖类中找,而这种执行方式是递归调用,由于B、C、E都没有因此去了A,然后在E,C,B的递归调用,因此在B的时候结果是1210。
类名继承的性质:
- 不用传递全部参数
- 初始化过程由自己控制
上述有一个问题,就是类名继承是会初始化两次的,因此在继承顺序中,后初始化的结果会把先计算的结果覆盖掉,参考如下:
class A:
def __init__(self, a1):
self.a = a1
self.self_a = 10
print("A")
def show(self):
print(self, "A show()")
class B(A):
def __init__(self, b1, b2, *args):
# super(B, self).__init__(*args)
A.__init__(self, 10)
self.b1 = b1
self.b2 = b2
# print("B", self.self_a)
print("B")
self.self_a += 100
def show(self):
print(self, "B show()")
class C(A):
def __init__(self, c1, c2, *args):
# super(C, self).__init__(*args)
A.__init__(self, 20)
self.c1 = c1
self.c2 = c2
print("C")
# self.self_a += 10000
def show(self):
print(self, "C show()")
class D(C, B):
def __init__(self, d1, d2, *args):
# super(D, self).__init__(*args)
# C.__init__(self, 3, 5) # 先初始化C,不会覆盖B的累加结果
# print("C", self.self_a) # self_a的输出结果为110
B.__init__(self, 1, 2)
print("B", self.self_a)
C.__init__(self, 3, 5) # 由于重新初始化会覆盖B累加的结果
print("C", self.self_a) # self_a的输出结果为10
self.d1 = d1
self.d2 = d2
print("D")
def show(self):
print(self)
print("D show()")
d = D(1, 2, 3, 4, 5, 6, 7)
# d.show()
print(D.__mro__)
print(d.self_a)
"""
A
B
B 110
A
C
C 10
D
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
10 #
"""
四、总结
super继承和类名继承的方法,会按照从自己出发逐步往父类走,走到第一个就算结束。对于变量而言,会递归进行,其顺序和类名继承的顺序一致。而Python采取的是C3的继承算法。注:虽然多继承的问题可以通过逐步分析,以及调整代码的顺序解决问题,但是由于实际工程中其过于复杂,尽可能不使用,采取更加优化的解决方法为好,并且通过设计模式和规范进行约束。例如将属性私有化,不允许修改。