继承包含私有成员的类

2 投票
3 回答
2322 浏览
提问于 2025-04-11 09:22

Python 有一个很棒的特点,就是你可以很简单地给变量起名字,甚至可以和访问器(accessor)同名。

self.__value = 1

def value():
    return self.__value

有没有简单的方法可以让我们访问一个类的私有成员,而我又想要继承这个类?很多时候,我希望能直接操作类里面的原始数据对象,而不需要一直使用访问器和修改器。

我知道这样做似乎有点违背私有和公共的基本原则,但通常我想要继承的类是我自己写的,我很乐意把它的成员暴露给子类,但不想让这个类的实例访问到这些成员。有没有什么好的方法可以做到这一点?

3 个回答

1

我不太确定这个说法的来源,但关于访问保护,Python 里有一句经典的话:“我们都是成年人了”。

正如 Thomas Wouters 所说,前面加一个下划线是标记一个属性属于对象内部状态的常用方式。而加两个下划线则是为了让这个属性的名字变得复杂一点,以防止别人轻易访问。

之后,你只需要期待使用你库的用户不会去碰那些“私有”的属性,自己给自己带来麻烦。

3

“我知道这似乎和私有和公共的基本概念相悖。”其实并不是“相悖”,只是和C++和Java的处理方式不同。

在C++和Java中,私有(private)的概念并不是特别有用。它有时可以帮助我们隔离一些实现细节,但用得太多了。

在Python中,以两个__开头的名字是特殊的,作为普通用户,你不应该随便定义这样的属性名。带有__的名字是特殊的,属于实现的一部分,并且是可以被你使用的。

以一个_开头的名字是“私有”的。它们有时会被稍微隐藏起来。大多数情况下,遵循“同意的成年人”规则——不要傻傻地使用它们,因为它们可能会在没有通知的情况下发生变化。

我们把“私有”放在引号里,是因为这只是你和你的用户之间的一个约定。你用_标记了某些东西,你的用户(包括你自己)应该尊重这个约定。

我们通常会在方法的函数名之前加一个_,表示我们认为这些方法是“私有的”,并且可能会在没有通知的情况下发生变化。

Java中那种无休止的获取器和设置器在Python中并不常用。Python的自省(introspection)功能更灵活,你可以访问对象内部的属性值字典,还有一些一流的函数,比如getattr()setattr()

此外,还有property()函数,通常用来将获取器和设置器绑定到一个名字上,这个名字看起来像一个简单的属性,但实际上是经过良好定义的方法调用。

5

其实并不方便,而且还会破坏封装性。双下划线的属性名会被处理成一个特殊的名字,前面加上类名,比如说你有一个叫做 'ContainerThing' 的类,它里面有一个 '__value' 的属性,这个属性实际上会被存储为 '_ContainerThing__value'。如果你改变了类名,或者重新安排这个属性的位置,那么所有试图访问这个属性的子类都会受到影响。

这就是为什么双下划线的命名方式(其实并不是真正的“私有”,只是“麻烦”)不太好用的原因。你只需要在属性名前加一个单下划线。这样大家都知道不要去碰你的“私有”属性,而且你在子类和其他需要用到的地方也能方便地访问它。双下划线属性的命名方式主要是为了避免属性名冲突,但这种情况其实非常少见。它并没有提供额外的“安全性”,因为即使是经过处理的属性名也很容易被访问。

顺便说一下,'__value' 和 'value'(还有 '_value')是不同的名字。下划线是名字的一部分。

撰写回答