Python与super一起使用孙子继承父级和子级

时间:2018-08-28 12:50:53

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

[使用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],有什么解决方法?

1 个答案:

答案 0 :(得分:3)

您误解了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

  

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

通过为__aux_func使用类私有命名,在Parent上定义的方法中对它的任何引用都将查找并找到_Parent__aux_func,并在{{1 }}会查找并找到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()。类专用名称实际上只是在打算由第三方代码子类化的API中意图,而对子类可以使用的名称没有太多限制。

相关问题