多继承(钻石继承)的问题和解决
大家都知道继承的目的是为了让子类可以使用父类的成员,实现代码的复用,但是在多继承中会出现各种问题:
class Father(object): def __init__(self, name): self.name = name print("Im father") class Son_1(Father): def __init__(self, age, name): self.age = age Father.__init__(self, name) print("Im Son_1") class Son_2(Father): def __init__(self, gender, name): self.gender = gender Father.__init__(self, name) print("我是Son_2") class GrandSon(Son_1, Son_2): def __init__(self, name, age, gender): Son_1.__init__(self, age, name) Son_2.__init__(self, gender, name) pass grand_son = GrandSon("张三", 18, "男") print(GrandSon.__mro__)
在上面的代码里,写了四个类,并且他们的继承关系非常清楚,Son1和Son2都继承自Father,而GrandSon刚好继承Son1,Son2,这样就是经典的菱形继承也叫钻石继承,
钻石继承带来的问题就是子类会调用多次父类的__init__方法,造成资源浪费和重复执行的问题。
上面的代码执行结果:这个grand_son = GrandSon('张三', 18, '男')一执行,打印了两次I am father
Im father Im Son_1 Im father 我是Son_2 (<class '__main__.GrandSon'>, <class '__main__.Son_1'>, <class '__main__.Son_2'>, <class '__main__.Father'>, <class 'object'>)
那么我们如何解决呢?
答:利用super()调用父类的__init__
super()执行原理:
super(cls, instance)
1 super会先获取instance的__mro__列表,__mor__返回继承关系解析顺序列表
2 找到列表中cls的下一个类,返回
3 super()省略参数的时候只能调用__mro__列表中,cls的下一个类。
class Father(object): def __init__(self, name): self.name = name print("Im father") class Son_1(Father): def __init__(self, name, age, gender): self.age = age super(Son_1, self).__init__(name, gender) print("Im Son_1") class Son_2(Father): def __init__(self, name, gender): self.gender = gender super(Son_2, self).__init__(name) print("我是Son_2") class GrandSon(Son_1, Son_2): def __init__(self, name, age, gender): super(GrandSon, self).__init__(name, age, gender) print("我是GrandSon") grand_son = GrandSon("张三", 19, "男",) print(GrandSon.__mro__)
用super()确实能够解决父类__init__重复调用的问题,但是这个传参是个麻烦,这样一来,Son1和Son2的参数需要根据grandSon的参数来变化,而且不能写死,这样非常不合理。
Im father 我是Son_2 Im Son_1 我是GrandSon (<class '__main__.GrandSon'>, <class '__main__.Son_1'>, <class '__main__.Son_2'>, <class '__main__.Father'>, <class 'object'>)
如何解决传参困难的问题呢?
答:使用*args,和**kwargs解决传参麻烦,具体*args,和**kwargs是python里的特色,都是不定长参数,记得javascript里的不定长参数都保存在arguments对象里头,而他们都是相似的,只是*args是可变的位置参数列表,**kwargs是可变的关键字参数列表
class Father(object): def __init__(self, name, *args, **kwargs): self.name = name print("我是父类__init__") class Son_1(Father): def __init__(self, name, age, *args, **kwargs): print("我是Son_1的__init__") super(Son_1, self).__init__(name, *args, **kwargs) self.age = age class Son_2(Father): def __init__(self, name, gender, *args, **kwargs): print("我是Son_2的__init__") self.gender = gender super(Son_2, self).__init__(name, *args, **kwargs) class GrandSon(Son_1, Son_2): def __init__(self, name, age, gender): super(GrandSon, self).__init__(name, age, gender) def say_hello(self): print(self.name, self.age, self.gender) grand_son = GrandSon("老王", 24, "男")
这样的结果才是我们要的,代码结果,妈妈再也不要担心我的传参问题了
我是Son_1的__init__ 我是Son_2的__init__ 我是父类__init__