[使用 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从 Child 类解析。我尝试通过 MRO 过程,但无法解释从不同类调用的函数。有人可以帮我吗
- 为什么会这样? 实现 [父
- 主][父辅助] 的解决方法是什么?
你误解了super()
的作用。super()
不会更改self
引用的类型。super(..., self).method()
仍将传递对被调用方法的self
引用,因此在所有三种情况下,self
都是Grandchild()
实例。
这意味着在所有情况下,self.aux_func()
都遵循正常的属性解析顺序,对于Grandchild()
实例,self.aux_func()
将始终找到Child.aux_func
并调用它。
换句话说,唯一被更改的查找是您在super()
对象本身上查找的属性。如果需要更多此类更改,则需要再次使用super()
,或者需要为每个类aux_func()
函数指定不同的名称。一种方法是使方法类私有。
后者可以通过在开头(但不在末尾(用两个下划线命名函数来完成。然后在编译时更改此类名称,以在引用它的所有位置注入类名:
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())
请参阅标识符的保留类文档:
__*
类私有名称。此类别中的名称在类定义的上下文中使用时,将重写为使用损坏的形式,以帮助避免基类和派生类的"私有"属性之间的名称冲突。
和"标识符(名称("部分:
私有名称重整:当类定义中以文本方式出现的标识符以两个或多个下划线字符开头且不以两个或多个下划线结尾时,它被视为该类的私有名称。 在为私有名称生成代码之前,将转换为较长的形式。转换将插入类名,并在名称前面插入一个下划线,并删除前导下划线。例如,名为
Ham
的类中出现的标识符__spam
将转换为_Ham__spam
。此转换与使用标识符的语法上下文无关。
通过使用类私有命名__aux_func
,任何从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()
。类私有名称实际上只用于由第三方代码子类化的 API,对子类可以使用的名称没有太多限制。