最近在给孩子编写一款小游戏的时候发现了一个 问题,某些子类调用父类属性时会报错,提示未找到该属性。当时我很纳闷,就在网上搜索一些相关资料发现这方面的资料很少。所以今天就写下来 希望能帮到有需要的朋友。
ok,我们先随便写上几个类
class A():
def __init__(self):
self.aa = 11
class B():
def __init__(self):
self.bb = 22
class C(A,B):
def __init__(self):
self.cc = 33
c = C()
print(c.aa, c.bb, c.cc)
运行以后得到的提示为
Traceback (most recent call last):
File "/Users/yansong/Desktop/fjdz-new/test.py", line 17, in <module>
print(c.aa, c.bb, c.cc)
AttributeError: 'C' object has no attribute 'bb'
Process finished with exit code 1
这里提示没有bb这个属性,到这里我非常纳闷,明明已经继承了啊。
于是我去研究了一下python的继承逻辑。Python使用MRO(method resolution order方法解析顺序)解决基类搜索顺序问题。这里参考了https://blog.csdn.net/u013008795/article/details/90412084这篇文章
C3算法,解决了继承的单调性,它阻止创建之前版本产生二义性的代码。求得的MRO本质是为了线性化,且确定 了顺序。
单调性:假设有A、B、C三个类,C的mro是[C, A, B],那么C的子类的mro中,A、B的顺序一致就是单调的
也就是说子类继承于多个父类,__init__,是按继承顺序,哪个父类在最前面且有自己的__init__,则继承它;若最前面的父类无__init__,则继承第二个父类的__init__,若还是无__init__,则依次往后寻找,直到继承的某个父类含__init__。
所以问题就出在这里,当C执行了父类A的构造器__init__()时,就不会执行B的构造器__init__()了。
于是我更改了一下继承顺序
class A():
def __init__(self):
self.aa = 11
class B():
def __init__(self):
self.bb = 22
class C(A,B):
def __init__(self):
super().__init__()
B.__init__(self)
self.cc = 33
现在我们再来打印一下之前无法找到的那个属性。
c = C()
print(c.bb)
果然打印出来了
22
Process finished with exit code 0
ok到目前为止,我们现在已经解决了调用之前无法找到属性。可是,如果我们如果想让两个父类的属性都被调用改怎么办呢??
其实办法也很简单,就是在子类的构造器里显式的调用一遍另一个负累的构造器。ok我们回到最初那段代码来试试
class A():
def __init__(self):
self.aa = 11
class B():
def __init__(self):
self.bb = 22
class C(A,B):
def __init__(self):
super().__init__()
B.__init__(self) #这里 我们显式的调用父类B的构造器
self.cc = 33
c = C()
print(c.aa, c.cc, c.bb)
结果为
11 33 22
Process finished with exit code 0
大功告成。到这里,基本上我们就解决了多继承时父类属性丢失的问题。
希望能对有同样问题的朋友们有一点帮助。最后说一句,多继承方案,其实并不是一个高效的方案,很多语言都不支持多继承,比如java,因为这样会增加后期维护,以及查找错误的难度。所以,慎用!!