对象名称前单下划线和双下划线的含义是什么?

1845 投票
18 回答
794579 浏览
提问于 2025-04-15 13:43

在Python中,单个和双个下划线放在一个对象名字前面,分别代表什么呢?

18 个回答

384

到目前为止,大家的回答都很棒,但还有一些小细节没有提到。一个前导下划线并不只是个约定:如果你使用 from foobar import *,而模块 foobar 没有定义 __all__ 列表,那么从这个模块导入的名字不会包括那些带有前导下划线的名字。可以说这主要是个约定,因为这种情况比较少见;-).

前导下划线的约定不仅仅用于表示私有名字,还用于 C++ 所称的保护名字——例如,某些方法的名字是为了让子类重写的(甚至是必须重写的,因为在基类中它们会 raise NotImplementedError!)通常会用一个前导下划线来表示,这样使用这个类(或子类)实例的代码就知道这些方法不应该被直接调用。

举个例子,如果要创建一个线程安全的队列,并且想要不同于先进先出的排队规则,可以导入 Queue,继承 Queue.Queue,并重写 _get_put 这些方法;“客户端代码”不会直接调用这些(“钩子”)方法,而是调用像 putget 这样的(“组织”)公共方法(这被称为 模板方法 设计模式——可以参考 这里,这是我关于这个主题的演讲视频的有趣介绍,附带了讲稿的摘要)。

编辑:演讲描述中的视频链接现在已经失效。你可以在 这里这里 找到前两个视频。

552
  • _foo:这只是个约定。程序员用它来表示这个变量是私有的(在Python中这意味着什么呢?)。

  • __foo:这有实际意义。解释器会把这个名字替换成_classname__foo,这样可以确保这个名字不会和其他类中相似的名字冲突。

  • __foo__:这也是个约定。Python系统用这种方式来使用一些名字,以避免和用户定义的名字发生冲突。

在Python中,其他形式的下划线没有特别的意义。而且在这些约定中,类、变量、全局等之间没有区别。

1617

单下划线

在一个类里,名字前面有下划线的属性或方法是告诉其他程序员,这些东西是打算在这个类内部使用的。不过,这并不意味着它们是完全私有的。

如果在模块中的函数名前加下划线,表示这个函数不应该被其他地方导入。

根据PEP-8风格指南:

_single_leading_underscore:这是一个弱的“内部使用”标志。例如,from M import *不会导入名字以下划线开头的对象。

双下划线(名称重整)

根据Python文档

任何形式为__spam的标识符(至少两个前导下划线,最多一个后导下划线)会被文本替换为_classname__spam,其中classname是当前类的名字,前面的下划线会被去掉。这种重整不考虑标识符的位置,所以可以用来定义类内部私有的实例变量、类变量、方法,甚至是存储在全局变量中的变量,以及在其他类的实例中存储的变量。

同一页面上的警告:

名称重整的目的是为了让类能够轻松定义“私有”的实例变量和方法,而不必担心派生类定义的实例变量,或者外部代码对实例变量的干扰。需要注意的是,这些重整规则主要是为了避免意外;仍然有可能有人能够访问或修改被认为是私有的变量。

示例

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}

撰写回答