在Python中,对象名称前的单前导下划线和双前导下划线代表什么?
当前回答
Python中不存在只能从对象内部访问的“私有”实例变量。然而,大多数Python代码都遵循一个惯例:前缀为下划线的名称(例如_spam)应被视为API的非公共部分(无论是函数、方法还是数据成员)。应将其视为实施细节,如有更改,恕不另行通知。
参考https://docs.python.org/2/tutorial/classes.html#private-变量和类本地引用
其他回答
.variable是半私有的,仅用于约定
.__variable通常被错误地认为是超级私有的,而它的实际含义只是为了命名mangle以防止意外访问[1]
.__variable__通常为内置方法或变量保留
您仍然可以访问__如果您非常想的话,可以对变量进行损坏。双下划线只是将变量命名为mangles或重命名为instance_类名__已损坏
例子:
class Test(object):
def __init__(self):
self.__a = 'a'
self._b = 'b'
>>> t = Test()
>>> t._b
'b'
t.b是可访问的,因为它仅按惯例隐藏
>>> t.__a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'
找不到t.__a,因为它由于名称损坏而不再存在
>>> t._Test__a
'a'
通过访问实例_className__variable而不仅仅是双下划线名称,您可以访问隐藏值
既然这么多人都在提到雷蒙德的谈话,我就把他说的话写下来,让事情变得简单一点:
双下划线的用意不是关于隐私。我们的意图是这样使用它类圆(对象):def__init__(自身,半径):self.radius=半径定义区域(自身):p=自身__周长()r=p/math.pi/2.0返回math.pi*r**2.0定义周长(自身):return 2.0*math.pi*self.radius__周界=周界#本地参考等级轮胎(圆形):定义周长(自身):返回圆周长(自身)*1.25这实际上是隐私的反面,都是关于自由的。它使您的子类可以自由覆盖任何一个方法,而不破坏其他方法。
假设您没有在Circle中保留周界的本地参考。现在,派生类Tire覆盖了周长的实现,而不接触面积。当您调用Tire(5).area()时,理论上它应该仍然使用Circle.perimeter进行计算,但实际上它使用的是Tire.perimet,这不是预期的行为。这就是为什么我们需要Circle的本地参考。
但为什么用周长代替周长?因为_perimeter仍然给派生类重写的机会:
class Tire(Circle):
def perimeter(self):
return Circle.perimeter(self) * 1.25
_perimeter = perimeter
双下划线具有名称损坏,因此父类中的本地引用在派生类中被重写的可能性很小。因此“使您的子类可以自由覆盖任何一个方法而不破坏其他方法”。
如果您的类不会被继承,或者方法重写不会破坏任何东西,那么您就不需要__double_leading_underscore。
有时,您会看到一个带有前导下划线的元组,如
def foo(bar):
return _('my_' + bar)
在本例中,发生的情况是,_()是本地化函数的别名,该函数根据语言环境对文本进行操作以将其转换为适当的语言等。例如,Sphinx这样做,您可以在导入中找到
from sphinx.locale import l_, _
在sphinx.locale中,_()被指定为某个本地化函数的别名。
单个下划线
在类中,带前导下划线的名称向其他程序员表示该属性或方法将在该类中使用。然而,隐私权并未以任何方式强制执行。对模块中的函数使用前导下划线表示不应从其他地方导入。
从PEP-8风格指南:
_single_leading_underscore:“内部使用”指标较弱。例如,from M import*不会导入名称以下划线开头的对象。
双下划线(名称Mangling)
从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!'}
到目前为止,答案很好,但缺少一些花絮。一个前导下划线不仅仅是一个约定:如果使用from foobar import*,并且foobar模块没有定义__all__列表,那么从模块导入的名称不包括前导下划线的名称。假设这主要是一个惯例,因为这个案例是一个相当模糊的角落;-)。
前导下划线约定不仅广泛用于私有名称,也广泛用于C++所称的受保护的名称,例如,完全打算由子类重写的方法的名称(即使是必须重写的方法,因为在基类中它们引发NotImplementedError!-)通常是单前导下划线名称,以向使用该类(或子类)实例的代码指示不打算直接调用所述方法。
例如,要使线程安全队列具有与FIFO不同的排队规则,可以导入queue,将queue.queue子类化,并重写_get和_put等方法;“客户端代码”从不调用这些(“钩子”)方法,而是调用(“组织”)公共方法,如put和get(这被称为模板方法设计模式——例如,请参见此处,以获取基于我关于该主题的演讲视频的有趣演示,并添加了摘要)。
编辑:会谈描述中的视频链接现已断开。你可以在这里和这里找到前两个视频。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录