【问题标题】:Pythonic way of passing different arguments in multiple inheritance setup在多重继承设置中传递不同参数的 Pythonic 方式
【发布时间】:2020-03-04 05:20:41
【问题描述】:

考虑以下 sn-p:

class A:
    def __init__(self, a):
        self._a = a

class B:
    def __init__(self, b1, b2):
        self._b1 = b1
        self._b2 = b2

class C(B, A):
    def __init__(self, b1, b2, a):
        super().__init__(b1=b1, b2=b2, a=a)

然后会遇到如下错误:

TypeError: init() 得到了一个意外的关键字参数 'a'

为了解决这个问题,我初始化了超类“A”,如下:

class C(B, A):
    def __init__(self, b1, b2, a):
        super().__init__(b1=b1, b2=b2)
        A.__init__(self, a=a)

另一个解决方案:

class C(B, A):
    def __init__(self, b1, b2, a):
        super().__init__(b1=b1, b2=b2)
        super(B, self).__init__(a=a)

我通过试错的方式找到了这些解决方案。所以,我想知道将参数传递给多个超类的最优雅的方式是什么。

【问题讨论】:

  • @DeepSpace 我正在寻找这两种解决方案背后的逻辑。该帖子没有解释为什么没有将参数归因于相关的父类。
  • 因为super() 只能引用单个类,并且该类是使用 mro(或方法解析顺序)计算的。对此进行研究。
  • @DeepSpace 实际上__mro__ 背后的理念是能够以正确的顺序引用多个委托。所以,我不认为super() 意味着引用单个类。

标签: python-3.x multiple-inheritance superclass


【解决方案1】:

如果您的类将与其他不需要知道的类(特别是不需要或不关心它们的参数)组合在一起,您可以使用** Python 参数/参数传递语法将参数折叠和展开到字典中。

换句话说,你的代码应该是这样的:

class A:
    def __init__(self, a, **kwargs):
        self._a = a
        super().__init__(**kwargs)

class B:
    def __init__(self, b1, b2, **kwargs):
        self._b1 = b1
        self._b2 = b2
        super().__init__(**kwargs)

class C(B, A):
    def __init__(self, b1, b2, a):
        super().__init__(b1=b1, b2=b2, a=a)

还请注意,在您上面的基类__init__ 代码中,您没有调用super(),因此只会执行更接近的超类的__init__

super() 在 Python 中完成了多重继承正常工作所需的所有魔法。最完整的一篇文章仍然是Python's super considered Super

简而言之:无论何时创建一个类,无论是否使用多个父类,Python 都会为其创建一个 __mro__ 属性 - 这是该类的“方法解析顺序”,指示方法和将在祖先中搜索属性。

计算 MRO 本身的算法很复杂,有点想解释一下,但可以合理地信任它,因为它“只是做正确的事”。直到今天,我发现它完整描述的唯一地方是 15 多年前的 its original presentation in Python 2.3 documentation。 (可选阅读,因为它“做正确的事”。)

super() 所做的是创建一个代理对象,该对象将为原始调用类选择“mro”序列中的下一个类,并直接在其 __dict__ 中搜索方法 - 如果找不到,它将去“mro”的下一个条目。 所以,如果你只考虑类B,它的主体内部的super().__init__() 将调用object.__init__。如果此时“kwargs”为空,就像只使用它关心的参数调用B() 那样,这正是我们想要的。

B.__init__ 在由“B”和“A”组成的“C”类的链式super() 调用中运行时,B.__init__ 中的super() 将使用“C” MRO - 下一个类是“A” - 所以 A.__init__ 会使用所有未使用的关键字参数调用。

【讨论】:

  • 有了你的回答,我学会了上面的协同设计。但是仍然无法理解为什么在我的初始示例中,super() 没有在C 中删除简单的位置参数?为什么我们需要在AB中调用super()?在我的示例中,C.__mro__(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
  • 从技术上讲,在 this 示例中,您不需要从 A 调用 super() - 但这样做您将一无所获,同时保持您的项目未来的证明,如果您做 - 因为其他继承层次结构稍后可能会组合到您的子类中,并且可能某些类最终会产生一个超类,该超类最终会在其 MRO 中的“A”和“对象”之间结束。
  • 而且super() 不会自行“剥离”任何参数。每个类中的每个方法都负责获取所需的参数。命名参数显式地使语言本身将具有这些名称的参数归因于该参数,同时将所有其他参数作为字典保存在“kwargs”中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-25
  • 2015-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多