在 Python 类 内解决钻石继承问题

Resolving Diamond Inheritance within Python Classes

考虑以下 python 代码:

class Parent(object):
    def __init__(self, name, serial_number):
        self.name = name
        self.serial_number = serial_number


class ChildA(Parent):
    def __init__(self, name, serial_number):
        self.name = name
        self.serial_number = serial_number
        super(ChildA, self).__init__(name = self.name, serial_number = self.serial_number)

    def speak(self):
        print("I am from Child A")


class ChildB(Parent):
    def __init__(self, name, serial_number):
        self.name = name
        self.serial_number = serial_number
        super(ChildB, self).__init__(name = self.name, serial_number = self.serial_number)

    def speak(self):
        print("I am from Child B")


class GrandChild(ChildA, ChildB):
    def __init__(self, a_name, b_name, a_serial_number, b_serial_number):
        self.a_name = a_name
        self.b_name = b_name
        self.a_serial_number = a_serial_number
        self.b_serial_number = b_serial_number
        super(GrandChild, self).__init_( something )

当 运行 GrandChild 中的 super 函数时,格式化 __init__ 参数以便 ChildA 和 ChildB 都获得正确参数的正确方法是什么?

另外,如何从 GrandChild class 中访问 speak 方法的两个不同版本(ChildA 的版本和 ChildB 的版本)?

在 python 中,您 can 在您的 parent class(es):

的(之一)上显式调用特定方法
ChildA.__init__(self, a_name, a_serial)
ChildB.__init__(self, b_name, b_serial)

注意在这种方式调用的时候需要显式的加上self

您也可以像您一样使用 super() 方式,这将调用 "first" parent。确切的顺序是动态的,但默认情况下它将执行继承层次结构的 left-to-right、depth-first、pre-order scans。因此,您的 super() 呼叫只会在 ChildA 上呼叫 __init__

所以,当你从 grandchild 调用 super 时,ChildA__init__ 方法将被调用,因为 super 遵循 __mro__ 属性 (从左到右 parent 秒,然后是祖父 parent 秒 left-to-right,然后是曾祖父 parent 秒,...)

由于ChildAinit也调用了super,那么所有的super调用都会被链接起来,调用childb的__init__和最终 parent init.

要实现这一点,您的界面通常需要保持一致。那就是位置参数需要表示相同的东西,并且按顺序排列。

在情况并非如此的情况下,关键字参数可能效果更好。

class Parent:    
    def __init__(self, name, serial, **kwargs):
        self.name = name
        self.serial = serial

class ChildA(Parent):    
    def __init__(self, a_name, a_serial, **kwargs):
        self.a_name = a_name
        self.a_serial = a_serial
        super().__init__(**kwargs)

class ChildB(Parent):    
    def __init__(self, b_name, b_serial, **kwargs):
        self.b_name = b_name
        self.b_serial = b_serial
        super().__init__(**kwargs)


class GrandChild(ChildA, ChildB):
    def __init__(self):
        super().__init__(name = "blah", a_name = "a blah", b_name = "b blah", a_serial = 99, b_serial = 99, serial = 30)

另请注意,在您的代号中,序列号和序列号在所有 类 之间被重用为实例属性,这可能不是您想要的。