在 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 秒,...)
由于ChildA
的init也调用了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)
另请注意,在您的代号中,序列号和序列号在所有 类 之间被重用为实例属性,这可能不是您想要的。
考虑以下 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 秒,...)
由于ChildA
的init也调用了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)
另请注意,在您的代号中,序列号和序列号在所有 类 之间被重用为实例属性,这可能不是您想要的。