【问题标题】:Python using super with grandchild inheriting parent and childPython使用super与孙子继承父母和孩子
【发布时间】:2018-08-28 12:50:52
【问题描述】:

[使用Python3.6] 我有一个设计,其中孙子继承了(父母的)父母和孩子。

class Parent:
    def aux_func(self):
        return "[parent aux]"

    def main_func(self):
        print("[parent main]" + self.aux_func())


class Child(Parent):
    def aux_func(self):
        return "[child aux]"

    def main_func(self):
        print("[child main]" + self.aux_func())


class Grandchild(Child, Parent):
    @classmethod
    def do_something(cls):
        g = Grandchild()
        g.main_func()
        super(Child, g).main_func()
        Parent.main_func(g)

Grandchild.do_something()

结果是 -

[child main][child aux]
[parent main][child aux]
[parent main][child aux]

从 Parent 调用函数会导致 aux_func 从 Child 类解析。我试图通过 MRO 流程,但无法解释从不同类调用的函数。有人可以帮我吗

  1. 为什么会这样?
  2. 实现 [parent main][parent aux] 的解决方法是什么?

【问题讨论】:

  • 您将错误的课程传递给super()。传入 current 类,Grandchild.
  • @MartijnPieters - 感谢您的参与。问题是super(Grandchild, g).main_func() 给了我[child main][child aux]。我正在尝试调用两个父函数。
  • 好吧,self 仍然是 Grandchild() 类的一个实例。 super() 不会改变 self 的绑定对象。
  • @MartijnPieters - 这可以解释。谢谢!我正在尝试提出一种设计,允许我从父或子调用所有函数,但不能同时调用两者。有什么建议吗?

标签: python python-3.6 multiple-inheritance method-resolution-order


【解决方案1】:

您误解了super() 的作用。 super() 不会改变 self 引用的类型。 super(..., self).method() 仍会将 self 引用传递给被调用的方法,因此 self 在所有三种情况下都是 Grandchild() 实例。

这意味着在所有情况下,self.aux_func() 都遵循正常的属性解析顺序,对于 Grandchild() 实例,self.aux_func() 将始终找到 Child.aux_func 并调用它。

换句话说,唯一改变的查找是您在super() 对象本身上查找的属性。如果你需要更多这样的改变,你需要使用super()再次你需要给你的aux_func()函数每个类不同的名字。一种方法是使方法 class 私有

后者可以通过在开头使用两个下划线命名您的函数来完成(但不是在结尾处)。然后在编译时更改这些名称以将类名注入到它所引用的所有位置:

class Parent:
    def __aux_func(self):
        # class private to Parent
        return "[parent aux]"

    def main_func(self):
        # any reference to __aux_func *in this class* will use
        # the Parent class-private version
        print("[parent main]" + self.__aux_func())


class Child(Parent):
    def __aux_func(self):
        # class private to Child
        return "[child aux]"

    def main_func(self):
        # any reference to __aux_func *in this class* will use
        # the Child class-private version
        print("[child main]" + self.__aux_func())

Reserved classes of identifiers documentation

__*
类私有名称。此类别中的名称在类定义的上下文中使用时,会被重写以使用重整形式,以帮助避免基类和派生类的“私有”属性之间的名称冲突。

还有Identifiers (Names) section:

私有名称修改:当以文本形式出现在类定义中的标识符以两个或多个下划线字符开头并且不以两个或多个下划线结尾时,它被视为该类的私有名称.在为私有名称生成代码之前,私有名称会转换为更长的形式。转换插入类名,删除前导下划线并在名称前面插入一个下划线。例如,出现在名为Ham 的类中的标识符__spam 将转换为_Ham__spam。这种转换与使用标识符的语法上下文无关。

通过对__aux_func 使用类私有命名,在Parent 上定义的方法中对其的任何引用都将查找并找到_Parent__aux_func,并且在Child 中对同名的任何引用将查找并找到@ 987654346@。这两个名称不同,因此不会冲突:

>>> class Grandchild(Child, Parent):
...     @classmethod
...     def do_something(cls):
...         g = Grandchild()
...         g.main_func()
...         super(Child, g).main_func()
...         Parent.main_func(g)
...
>>> Grandchild.do_something()
[child main][child aux]
[parent main][parent aux]
[parent main][parent aux]

实现此目的的另一种方法是明确地使用不同的名称;说parent_aux_func()child_aux_func()。类私有名称实际上只是有意在旨在由第三方代码子类化的 API 中,而对子类可以使用的名称没有太多限制。

【讨论】: