如何:安全地调用具有不同参数的超级构造函数

Posted

技术标签:

【中文标题】如何:安全地调用具有不同参数的超级构造函数【英文标题】:How to: safely call super constructors with different arguments 【发布时间】:2022-01-17 01:58:09 【问题描述】:

我看到super().__init__(*args) 曾经安全地调用超级构造函数(以一种不会失败菱形继承的方式)。但是我找不到以这种方式调用具有不同参数的不同超级构造函数的方法。

这是一个说明问题的示例。

from typing import TypeVar, Generic

X = TypeVar("X")
Y = TypeVar("Y")

class Base:
  def __init__(self):
    pass

class Left(Base, Generic[X]):
  def __init__(self, x:X):
    super().__init__()
    self.lft = x

class TopRight(Base, Generic[Y]):
  def __init__(self, y:Y):
    super().__init__()
    self.rgh = y

class BottomRight(TopRight[Y], Generic[Y]):
  def __init__(self, y:Y):
    super().__init__(y + y)

class Root(Left[X], BottomRight[Y], Generic[X, Y]):
  def __init__(self, x:X, y:Y):
    pass #issue here

    #does not work
    #super().__init__(x)
    #super().__init__(y)

    #calls base twice
    #Left[X].__init__(x)
    #BottomRight[Y].__init__(y)

如何分别安全地呼叫Left.__init__(x)BottomRight.__init__(y)

【问题讨论】:

一方面,super().__init__() 调用LeftTopRight 是不行的,因为它们甚至不尊重Base 的签名。您可以传入 int 或将默认参数添加到 Base 以解决该问题。 我认为简短的回答是不要做这样的事情。像 Java 这样的东西不支持菱形继承是有原因的。 感谢您指出示例代码的问题,此外,多重继承仍然有用,特别是考虑到 python 的打字系统。 现在Base 有语法错误(你忘了pass 【参考方案1】:

问题在于,要以协作形式使用,中间类必须接受不是“针对”它们的参数,并通过自己的super 调用以透明的方式传递这些参数。

你他们不要多次调用你的祖先类:你让语言运行时为你做这件事。

你的代码应该写成:

from typing import Generic, TypeVar

X = TypeVar("X")
Y = TypeVar("Y")

class Base:
  def __init__(self):
      pass

class Left(Base, Generic[X]):
  def __init__(self, x:X, **kwargs):
    super().__init__(**kwargs)   
    self.lft = x

class TopRight(Base, Generic[Y]):
  def __init__(self, y:Y, **kwargs):
    super().__init__(**kwargs)
    self.rgh = y

class BottomRight(TopRight[Y], Generic[Y]):
  def __init__(self, y:Y, **kwargs):   # <- when this is executed, "y" is extracted from kwargs
    super().__init__(y=y + y, **kwargs)  # <-  "x" remains in kwargs, but this class does not have to care about it.

class Root(Left[X], BottomRight[Y], Generic[X, Y]):
  def __init__(self, x:X, y:Y):
      super().__init__(x=x, y=y)  # <- will traverse all superclasses,  "Generic" being last

另外,请注意,根据您项目的目的和最终的复杂性,这些类型注释可能不会给您带来任何好处,反而会增加代码的复杂性,否则会变得微不足道。它们在 Python 项目中并不总是有好处,尽管由于工具(即 IDE)的情况,可能会推荐它们。

另外,查看几天前类似的答案,我是否详细介绍了 Python 方法解析顺序机制,并指向它们的官方文档:In multiple inheritance in Python, init of parent class A and B is done at the same time?

【讨论】:

打败我。很不错!尤其是最后一点。鸭子打字在大多数时候都很棒。 不应该打电话给Rootsuper().__init__(x=x, y=y)吗? 确实如此。否则 kwargs 不会选择它们 也缺少括号。 super()super @MadPhysicist 我冒昧添加了括号

以上是关于如何:安全地调用具有不同参数的超级构造函数的主要内容,如果未能解决你的问题,请参考以下文章

创建对象向量时,不为每个对象唯一地调用默认对象构造函数

如何在 Python 中调用超级构造函数?

如何强制调用某些构造函数/函数以使用命名参数?

如何在龙目岛调用超级构造函数

构造函数的定义和构造函数的重载

这个设计模式有名字吗? (具有仅调用构造函数的实现的基类)