【问题标题】:Python method name with double-underscore is overridden?带有双下划线的 Python 方法名称被覆盖?
【发布时间】:2023-08-15 01:03:02
【问题描述】:

看看这个。 请注意,B 类会覆盖 Aa() 方法。

In [1]: class A(object):
   ...:     def __init__(self):
   ...:         self.a()
   ...:     def a(self):
   ...:         print "A.a()"
   ...:         
   ...:         

In [2]: class B(A):
   ...:     def __init__(self):
   ...:         super(B, self).__init__()
   ...:     def a(self):
   ...:         print "B.a()"
   ...:         
   ...:         

In [3]: b = B()
B.a()

没有惊喜。

现在,看看这个。 请注意,现在被覆盖的方法是__a()

In [7]: class A(object):
   ...:     def __init__(self):
   ...:         self.__a()
   ...:     def __a(self):
   ...:         print "A.__a()"
   ...:         
   ...:         

In [8]: class B(A):
   ...:     def __init__(self):
   ...:         super(B, self).__init__()
   ...:     def __a(self):
   ...:         print "B.__a()"
   ...:         
   ...:         

In [9]: b = B()
A.__a()

这种行为让我很吃惊。

谁能解释为什么调用A.__a() 而不是B.__a()

关于__a的任何__special__

更新: 在阅读了 Sean 的回答后,我想看看我是否可以覆盖 name mangled 方法并得到以下结果:

In [11]: class B(A):
   ....:     def __init__(self):
   ....:         super(B, self).__init__()
   ....:     def _A__a(self):
   ....:         print "B._A__a()"
   ....:         
   ....:         

In [12]: b = B()
B._A__a()

【问题讨论】:

    标签: python


    【解决方案1】:

    带有 __* 模式的关键字是类私有名称。

    http://docs.python.org/reference/lexical_analysis.html#reserved-classes-of-identifiers

    引用:

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

    私人名称修改(强调):

    私有名称修饰:当文本出现在类定义中的标识符以两个或多个下划线字符开头并且不以两个或多个下划线结尾时,它被视为该类的私有名称。在为私有名称生成代码之前,私有名称会转换为更长的形式。转换在名称前面插入类名,删除前导下划线,并在类名前面插入一个下划线。 例如,出现在名为 Ham 的类中的标识符 __spam 将转换为 _Ham__spam。此转换与使用标识符的语法上下文无关。如果转换后的名称非常长(超过 255 个字符),则可能会发生实现定义的截断。如果类名仅包含下划线,则不进行任何转换。

    http://docs.python.org/reference/expressions.html#atom-identifiers

    这意味着在幕后,B.__a() 被转换为类似B._B__a() 的东西

    【讨论】:

    • 手册中的正确位置正是我所寻找的。谢谢。
    【解决方案2】:
    In [1]: class A(object):
    ...:     def __init__(self):
    ...:         self.a()
    ...:     def a(self):
    ...:         print "A.a()"
    ...:
    ...:     __str__ = a
    ...:         
    
    In [2]: class B(A):
    ...:     def __init__(self):
    ...:         super(B, self).__init__()
    ...:     def a(self):
    ...:         print "B.a()"
    ...:         
    ...:         
    
    In [3]: b = B()
            print str(b)
       A.a()
    

    您需要在B中再次声明__str__

    【讨论】:

    • 不确定这里是否发生了同样的事情,因为 变量不像 __ 变量那样被破坏。