多继承(钻石继承)的问题和解决

大家都知道继承的目的是为了让子类可以使用父类的成员,实现代码的复用,但是在多继承中会出现各种问题:

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__