对象名称前单下划线和双下划线的含义是什么?
在Python中,单个和双个下划线放在一个对象名字前面,分别代表什么呢?
18 个回答
到目前为止,大家的回答都很棒,但还有一些小细节没有提到。一个前导下划线并不只是个约定:如果你使用 from foobar import *
,而模块 foobar
没有定义 __all__
列表,那么从这个模块导入的名字不会包括那些带有前导下划线的名字。可以说这主要是个约定,因为这种情况比较少见;-).
前导下划线的约定不仅仅用于表示私有名字,还用于 C++ 所称的保护名字——例如,某些方法的名字是为了让子类重写的(甚至是必须重写的,因为在基类中它们会 raise NotImplementedError
!)通常会用一个前导下划线来表示,这样使用这个类(或子类)实例的代码就知道这些方法不应该被直接调用。
举个例子,如果要创建一个线程安全的队列,并且想要不同于先进先出的排队规则,可以导入 Queue,继承 Queue.Queue,并重写 _get
和 _put
这些方法;“客户端代码”不会直接调用这些(“钩子”)方法,而是调用像 put
和 get
这样的(“组织”)公共方法(这被称为 模板方法 设计模式——可以参考 这里,这是我关于这个主题的演讲视频的有趣介绍,附带了讲稿的摘要)。
_foo
:这只是个约定。程序员用它来表示这个变量是私有的(在Python中这意味着什么呢?)。__foo
:这有实际意义。解释器会把这个名字替换成_classname__foo
,这样可以确保这个名字不会和其他类中相似的名字冲突。__foo__
:这也是个约定。Python系统用这种方式来使用一些名字,以避免和用户定义的名字发生冲突。
在Python中,其他形式的下划线没有特别的意义。而且在这些约定中,类、变量、全局等之间没有区别。
单下划线
在一个类里,名字前面有下划线的属性或方法是告诉其他程序员,这些东西是打算在这个类内部使用的。不过,这并不意味着它们是完全私有的。
如果在模块中的函数名前加下划线,表示这个函数不应该被其他地方导入。
根据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!'}