Python 对继承父子的孙子使用 super

Python using super with grandchild inheriting parent and child

[使用 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]

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

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

您误解了 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 设为私有 .

后者可以通过在开头(但不是末尾)使用两个下划线命名您的函数来完成。然后在编译时 更改这些名称 以在所有引用它的地方注入 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

__*
Class-private names. Names in this category, when used within the context of a class definition, are re-written to use a mangled form to help avoid name clashes between “private” attributes of base and derived classes.

Identifiers (Names) section:

Private name mangling: When an identifier that textually occurs in a class definition begins with two or more underscore characters and does not end in two or more underscores, it is considered a private name of that class. Private names are transformed to a longer form before code is generated for them. The transformation inserts the class name, with leading underscores removed and a single underscore inserted, in front of the name. For example, the identifier __spam occurring in a class named Ham will be transformed to _Ham__spam. This transformation is independent of the syntactical context in which the identifier is used.

通过对 __aux_func 使用 class 私有命名,从 Parent 上定义的方法对它的任何引用都将查找并找到 _Parent__aux_func,对Child 中的相同名称将查找并找到 _Child__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][parent aux]
[parent main][parent aux]

实现此目的的另一种方法是明确使用不同的名称;说 parent_aux_func()child_aux_func()。 Class 私有名称实际上仅 打算 在旨在由第三方代码子class 的 API 中,对子名称的名称没有太多限制[= =69=]可以用。