5

我写了一个python的测试代码,例如:

class Parent(object):
    @classmethod
    def dispatch(klass):
        print 'klass = %s' % klass
        return klass().__dispatch()
    def __dispatch(self):
        print 'This Parent.__dispatch()'
        print self


class Child(Parent):
    def __dispatch(self):
        print 'This Child.__dispatch()'
        print self


if __name__=='__main__':
    print 'Calling Parent.dispatch() ...\n'
    Parent.dispatch()
    print ''
    print 'Calling Child.dispatch() ...\n'
    Child.dispatch()
    print '\n-END'

输出是:

 Calling Parent.dispatch() ...

 klass = <class '__main__.Parent'> 
 This Parent.__dispatch()
 <__main__.Parent object at 0x0000000002D3A2E8>

 Calling Child.dispatch() ...

 klass = <class '__main__.Child'> 
 This Parent.__dispatch()
 <__main__.Child object at 0x0000000002D3A2E8>

 -END

为什么不调用 Child 类的覆盖方法 '__dispatch(self)' 很奇怪。

有人可以解释一下吗?

谢谢你。

4

2 回答 2

7

以双下划线开头的方法名会被自动修改;在内部,字符串_ClassName被添加到方法名称之前:

>>> class Foo(object):
...     def __bar(self):
...         pass
... 
>>> Foo.__dict__.keys()
['__dict__', '__module__', '__weakref__', '__doc__', '_Foo__bar']

此重命名也适用于在类中的任何其他方法中引用此方法名称的任何内容。

因此,您的__dispatch()方法被重命名为_Parent__dispatch(),并且该dispatch()方法被更改为 call self._Parent__dispatch()。同样,您的Child类有一个_Child__dispatch()方法,因此它不会覆盖_Parent__dispatch()它的 super-class 的方法

这就是为什么你看到你看到的结果;将您的__dispatch()方法重命名为_dispatch()(只有一个下划线),它将按预期工作。

为什么python会这样做?它是一种提供私有属性和方法的形式,不会被继承自它们的类意外覆盖。请参阅Python 表达式参考中的私有名称修饰

PEP 8 Python 样式指南对私有名称修改有这样的说法:

如果您的类打算被子类化,并且您有不希望子类使用的属性,请考虑使用双前导下划线命名它们并且没有尾随下划线。这调用了 Python 的名称修饰算法,其中类的名称被修饰为属性名称。如果子类无意中包含具有相同名称的属性,这有助于避免属性名称冲突。

注意 1:请注意,重命名中只使用了简单的类名,因此如果子类选择相同的类名和属性名,仍然会出现名称冲突。

注意 2:名称修饰可以使某些用途,例如调试和 __getattr__(),不太方便。然而,名称修饰算法有据可查,并且易于手动执行。

注 3:不是每个人都喜欢名字修饰。尝试在避免意外名称冲突的需要与高级呼叫者的潜在使用之间取得平衡。

于 2012-09-15T14:03:10.390 回答
1

您是否尝试过调用方法 _dispatch 而不是 __dispatch?

关键是 __dispatch 在内部被翻译成 _NameOfTheClass_dispatch (或某种提供某种“私有”成员函数行为的东西)。这就是您的覆盖失败的原因。

如果您使用 _dispatch,则成员函数的名称不受下划线的影响,并且程序按照您的意愿运行。

于 2012-09-15T14:03:05.710 回答