组合类及其组件(Python)中的同时多重继承



早些时候,我在两个类C1C2之间有一个简单的单继承架构,效果很好:

class C1:
def __init__(self, x, y):
self.x = x
self.y = y
print("C1")
class C2(C1):
def __init__(self, z, *args):
C1.__init__(self, *args)
self.z = z
print("C2")
c2 = C2(1, 2, 3) # prints "C1" and "C2"
print(c2.x, c2.y, c2.z) # prints 2 3 1

现在架构变得更加复杂:

class _B1: # abstract base
def __init__(self, x):
self.x = x
print("B1")
class _B2(_B1): # concrete base
def __init__(self, z, *args):
_B1.__init__(self, *args)
self.z = z
print("B2")
class _M1: # abstract mixin
def __init__(self, y):
self.y = y
print("M1")
class _M2(_M1): # concrete mixin
def __init__(self, *args):
_M1.__init__(self, *args)
print("M2")
class C1(_M1, _B1): # abstract composed
def __init__(self, x, y): # old signature must not be changed
_B1.__init__(self, x)
_M1.__init__(self, y)
print("C1")
class C2(_M2, _B2, C1): # concrete composed; use C1 here because isinstance(c2, C1) must still return True
def __init__(self, z, *args): # old signature must not be changed
C1.__init__(self, *args) # works
_B2.__init__(self, z, x) # Problem 1a: cannot do that because x is abstracted in *args
_M2.__init__(self, y) # Problem 1b: cannot do that because y is abstracted in *args
# Problem 2: there seem to be "two versions" of B1 and M1 (one from C1 and one from B2 or M2, resp.), and so the constructors are each called twice
print("C2")
# c2 = C2(1, 2, 3)
# print(c2.x, c2.y, c2.z)

如代码中所述,我无法弄清楚如何将参数传递给构造函数。此外,构造函数被调用两次的事实让我觉得这是一个糟糕的设计;但是,从OOP的角度来看,我想不出更准确的方法。

我知道一些解决方法,但我更喜欢规范的解决方案。特别是,我不想在C2.__init__中包含xy.

这就是super存在的原因。

class _B1: # abstract base
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self.x = x
print("B1")

class _B2(_B1): # concrete base
def __init__(self, z, **kwargs):
super().__init__(**kwargs)
self.z = z
print("B2")

class _M1: # abstract mixin
def __init__(self, y, **kwargs):
super().__init__(**kwargs)
self.y = y
print("M1")

class _M2(_M1): # concrete mixin
def __init__(self, **kwargs):
super().__init__(**kwargs)
print("M2")

class C1(_M1, _B1):
def __init__(self, **kwargs):
super().__init__(**kwargs)
print("C1")

class C2(_M2, _B2, C1):
def __init__(self, **kwargs):
super().__init__(**kwargs)
print("C2")

c2 = C2(x=1, y=2, z=3)
print(c2.x, c2.y, c2.z)

输出:

B1
M1
C1
B2
M2
C2
1 2 3

需要注意的一些事项:

  1. 每个__init__都接受任意关键字参数,并将任何它不处理的参数传递给super().__init__
  2. 每个__init__调用super.__init__一次;正确定义,将访问层次结构中的每个类。
  3. 如果操作正确,**kwargs在调用object.__init__时将为空。例如,当调用C2.__init__时,它的kwargs包含x=1y=2z=3,所有这些都被传递给M2.__init__,将它们传递给B2.__init__。因为B2.__init__按名称声明z,所以它的kwargs只包含x=1y=2,所以这些被传递,但z不是。 在这种情况下,B1是调用object.__init__的类,但此时xyz中的每一个都被一个或另一个方法"消费"了。
  4. 当你实际实例化C2时,你使用关键字参数来避免担心哪个位置参数由哪个方法处理。
  5. 如果你删除了对print的调用,你根本不需要定义C2.__init__C1.__init__M2.__init__

相关内容

  • 没有找到相关文章

最新更新