解决 Python 类中的钻石继承

问题描述 投票:0回答:3

考虑以下 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 类中访问

speak
方法的两个不同版本(ChildA 的版本和 ChildB 的版本)?

python python-3.x inheritance multiple-inheritance diamond-problem
3个回答
13
投票

所以,当你从孙子调用 super 时,

ChildA
__init__
方法将被调用,因为 super 遵循
__mro__
属性(父母从左到右然后祖父母从左到右,然后是曾祖父母,.. .)

由于

ChildA
init也调用了super,那么所有的super调用将被链接起来,调用子b的
__init__
并最终调用父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)

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


3
投票

在 python 中,您 can 显式调用父类(其中一个)的特定方法:

ChildA.__init__(self, a_name, a_serial)
ChildB.__init__(self, b_name, b_serial)

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

self

你也可以 - 就像你一样 - 使用

super()
方式,这将调用“第一个”父母。确切的顺序是动态的,但默认情况下,它将对继承层次结构进行从左到右、深度优先、预排序扫描。因此,您的
super()
电话只会在
__init__
上拨打
ChildA


0
投票

Diamond 问题在 Python 中不存在,因为它优先考虑最先继承的类。

class Parent:    
    def __init__(self, name, serial, **kwargs):
        self.name = name
        self.serial = serial
        
    def printName(self):
        print("Inside P");

class ChildA(Parent):    
    def __init__(self, a_name, a_serial, **kwargs):
        self.a_name = a_name
        self.a_serial = a_serial
        
    def printName(self):
        print("Inside A");

class ChildB(Parent):    
    def __init__(self, b_name, b_serial, **kwargs):
        self.b_name = b_name
        self.b_serial = b_serial
        
    def printName(self):
        print("Inside B");


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)
          
obj = GrandChild();
obj.printName();

输出是A内

在上面的例子中,GrandChild(ChildA, ChildB)表示类GrandChild先继承类ChildA,再继承类ChildB。

一旦你在 D 中声明了 printName() 方法,那么输出将是 Inside GrandChild。如果你在 GC 类中交换继承子的位置,那么:

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

输出是B里面

© www.soinside.com 2019 - 2024. All rights reserved.