【问题标题】:Override a "private" method in Python覆盖 Python 中的“私有”方法
【发布时间】:2013-01-31 15:13:33
【问题描述】:

考虑一个具有“私有”方法的类,例如:

class Foo(object):
    def __init__(self):
        self.__method()
    def __method(self):
        print('42')

当我尝试继承Foo 并覆盖方法__method 时,可以看到Foo.__method 仍然被调用,而不是MoreFoo.__method

class MoreFoo(Foo):
    def __method(self):
        print('41')

>>> MoreFoo()
42
<__main__.MoreFoo object at 0x7fb726197d90>

重写这种方法的方法是什么?

【问题讨论】:

  • 出于兴趣,您从哪里得知这相当于一个私有变量?这是一个非常糟糕的误解,我一直看到它,不知道它来自哪里。
  • @Lattyware -- 我认为它来自名为“私有变量和类本地引用”的文档部分。它继续说没有私有变量......但是双下划线是你能得到的最接近的东西——“因为类私有成员有一个有效的用例(即避免名称的名称冲突名称由子类定义),对这种机制的支持有限,..."

标签: python oop methods subclass private-members


【解决方案1】:

使用双下划线命名约定的要点是为了防止子类(意外)覆盖该方法。

如果您无论如何必须覆盖它,那么有人犯了严重的类设计错误。您仍然可以这样做,只需这样命名您的对象:

def _Foo__method(self):

在方法名称的前面加上一个下划线和定义的类名(因此在这种情况下以_Foo 为前缀)。用双下划线重命名方法和属性的过程称为private name mangling

如果您想定义在子类中仍可被覆盖的“私有”方法,您通常坚持使用带有一个下划线的名称 (def _method(self):)。

由于其动态特性,Python 中几乎没有什么是真正私有的;稍加反省,几乎可以达到任何目标。

【讨论】:

    【解决方案2】:

    双下划线前缀调用名称修改。它等同于私有方法,专门设计用于避免被子类覆盖(在这种情况下,方法名称变为_Foo__method)。

    如果您有实现细节,请在其前面加上一个下划线。这是不可在外部使用的 Python 方法的普遍接受的标志。没有强制执行,因为这不是 Python 运行的方式。

    【讨论】:

    • 我知道它不是真正的私有,这就是为什么我在引号之间写私有。
    • @ErnestA 这不仅仅是因为它不是真正的私有的,而是它绝不相似或可比 - 它扮演着完全不同的角色。
    • 我的印象是,调用以下划线“私有”开头的方法是可以的。也许不是。在这种情况下,很抱歉使用了错误的术语。
    • 它与私有变量并不“完全不同”。 __ 表示作者的意图,名称修改增加了覆盖的障碍,但随后就可以了。
    • @tdelaney 是的,但_ 表明作者的意图是它是一个实现细节。 __ 表示它们的子类可能会不小心弄乱它们的内部结构。完全不同。将其描述为私人只会混淆和误导。
    【解决方案3】:

    我相信你会做这样的事情:

    class MoreFoo(Foo):
        def _Foo__method(self):
            print('41')
    

    作为documented

    __spam 形式的任何标识符(至少两个前导下划线,最多一个尾随下划线)在文本上替换为_classname__spam,其中classname 是当前类名,前导下划线被去除。

    但是原始类的设计者认为你不应该做这样的事情,因为难以用子类覆盖是双下划线开头的重点:

    名称修改有助于让子类覆盖方法而不破坏类内方法调用。

    【讨论】:

    • 从技术上讲,这应该可行,但它首先掩盖了滥用双下划线的更严重问题。
    • @AndrewGorcester -- 当然。我并不是说这是个好主意。
    • 谢谢。我没有写原始类。我只想覆盖它的一个方法,它恰好以两个下划线开头。
    • @ErnestA -- 很公平。我将其更改为说原始课程的设计者不希望您这样做;-)。你可以做任何你想做的事——正如成语所说:“我们都是同意的成年人”
    猜你喜欢
    • 2014-05-28
    • 2011-01-01
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    相关资源
    最近更新 更多