naming-conventions - 有人能请解释前导下划线在Python中的对象名称之前的确切含义吗?

  显示原文与译文双语对照的内容

我想把这一切清除一次。 有人可以解释在 python 中的对象名称之前有前导下划线的确切含义? 同时解释一个单行下划线和双下划线的区别。 同样,如果问题是一个变量,一个函数,一个方法,等等?

时间:

单下划线

在类中,带有前导下划线的名称只是向其他程序员表明属性或者方法是私有的。 但是,名称本身没有什么特别的。

引用 PEP-8 web:

_single_leading_underscore: 弱"内部使用"指示器。 yf_terminology_E.g. @#@#@#比如 _yf_terminology from M import * 不导入名称以下划线开头的对象。

双下划线( 名称重整)

中,python 文档:

表单 __spam ( 至少有两个前导下划线,至少有一个尾随下划线)的任何标识符都用 _classname__spam 替换,其中 classname 是当前类名,并去掉前导下划线。 这个过程不考虑标识符的句法位置,所以可以用来定义class-private实例和类变量,方法,存储在全局变量中的变量,甚至存储在实例中的变量。 在其他类实例上私有到此类。

来自同一页面的警告:

名称重整旨在给类一个简单的方法来定义"专用的"实例变量和方法,而不必担心派生类定义的实例变量,或者使用类外部的代码搞乱实例变量。 注意,重整规则的设计主要是为了避免意外;决定的灵魂可以访问或者修改一个被认为是私有的变量。

例子


>>> 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__ underscore,和模块列表,这些名称从该模块导入包含 those. 让我们说这是大多一个约定,因为这种情况下是一个很阴暗的角落;- ) 。

为 leading-underscore约定如下广泛的应用不仅仅是私人,而且还为了什么 C++ 名称就叫保护 --例如名称由子类的方法,只是完全用于被重写( 在基类中他们 raise NotImplementedError -,即使是那些以来已经要重写) 通常single-leading-underscore名称来指示到该类的代码使用 实例( 或者子类),说方法不应直接调用。

例如要让一个thread-safe队列有着不同的失败比 FIFO,一个语句导入队列纪律,Queue.Queue, 子类并继承自此类方法作为 _get_put ;"客户端代码"永不会调用那些("钩") 方法,而是这些组织") ("公共方法,如 putget ( 有的增加了 synopses transcript, 这就是所谓的模板方法设计模式--看到 比如 这里对于一个有趣的表述上的一段视频,将谈话的基础上我的主题,) 。

__foo__: 这只是一个惯例,一个 python 系统使用不会与用户名冲突的名称的方法。

_foo: 这是一个惯例,程序员用来表示变量是私有的。

__foo: 这有真正的含义: 解释器用 _classname__foo 替换这里名称,以确保名称不会与另一个类中的相似名称重叠。

在 python 世界中没有其他形式的下划线有意义。

这些约定中的类,变量,全局等都没有区别。

._variable 是 semiprivate,仅用于约定

.__variable 被认为是superprivate并获取namemangled以防止意外访问

.__variable__ 通常为内置方法或者变量保留

你仍然可以访问 .__superprivate 变量,如果你愿意。 双下划线仅仅是 namemangles,或者重命名为类似于 instance._className__superprivate的变量

例如:


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,因为它不再存在于 namemangling


>>> t._Test__a
'a'

通过访问 instance._className__variable 而不是superprivate名称,你可以访问隐藏的值

有时你会看到一个带有前导下划线的元组,如下所示


def foo(bar):
 return _('my_' + bar)

在这种情况下,_() 是一个本地化函数的别名,它在文本上操作,将它的放入适当的语言,基于语言环境的等等 。 比如,Sphinx这样做,你会在导入中发现


from sphinx.locale import l_, _

在 sphinx 。locale中,_() 被指派为一些本地化函数的别名。

在开头加一磅下划线:

python 没有真正的私有方法,所以在方法或者属性的开头有一个下划线意味着你不应该访问这个方法,因为它不是API的一部分。


class BaseForm(StrAndUnicode):

 def _get_errors(self):
"Returns an ErrorDict for the data provided for the form"
 if self._errors is None:
 self.full_clean()
 return self._errors

 errors = property(_get_errors)

代码Fragment取自django源代码 (django/forms/forms.py). 这意味着这里属性错误是一个属性,它是该模块的一部分,但是该方法调用,_get_errors,是"专用的",所以你不应该访问它。

开头加两个下划线:

这会引起很多混乱。 不应用于创建私有方法。 它应该用来避免你的方法被子类覆盖。 我们来看一个例子:


class A(object):
 def __test(self):
 print"I'm test method in class A"

 def test(self):
 self.__test()

a = A()
a.test()

输出:

$ python test.py

我是类A 中的测试方法

现在创建一个子类B 并对__test方法进行定制


class B(A):
 def __test(self):
 print"I'm test method in class B"

b = B()
b.test()

输出将是。。

$ python test.py

我是类A 中的测试方法

就像我们看到的,A.test() 没有调用 B.__test() 方法,就像我们可以预料的。 基本上它是__的正确行为。 所以当你创建一个以__开头的方法时,这意味着你不希望任何人都可以重写它,它只能从自己的类中访问。

在开头和结尾加两个下划线:

当我们看到像 __this__ 这样的方法时,不要调用它。 因为这意味着它是 python 调用的方法,而不是你。 让我们看看:


>>> name ="test string"
>>> name.__len__()
11
>>> len(name)
11

>>> number = 10
>>> number.__add__(40)
50
>>> number + 50
60

总是有一个操作符或者原生函数调用这些魔术方法。 有时它只是在特定情况下的钩子 python 调用。 例如在创建对象时调用 __init__() 。 调用 __new__() 以生成实例。。

让我们举个例子。。


class FalseCalculator(object):

 def __init__(self, number):
 self.number = number

 def __add__(self, number):
 return self.number - number

 def __sub__(self, number):
 return self.number + number



number = FalseCalculator(20)
print number + 10 # 10
print number - 20 # 40

有关详细信息,PEP-8指南 。将帮助更多。

请在 python 中找到更多的魔术方法。 http://www.rafekettler.com/magicmethods.pdf

如果你真的想做一个变量 read-only,那么最好的方法是使用 property(),只使用getter传递给它。 使用 property(),我们可以完全控制数据。


class PrivateVarC(object):

 def get_x(self):
 pass

 def set_x(self, val):
 pass

 rwvar = property(get_p, set_p) 

 ronly = property(get_p) 

我明白,OP问了一个不同的问题,但是我发现另外一个问题,要求我用这个来标记'如何设置私有变量',我想在这里添加额外的信息。

单个前导下划线是一个约定。 如果名称以单个下划线或者不是一个下划线开头,就没有区别。

双前导和尾随下划线用于内置方法,如 __init____bool__ 等。

两个前导下划线( w/o 尾随) 是一个约定,但是,类方法将被解释器修改为 对于变量或者基本函数名不存在差异。

你的问题很好,不仅仅是方法。 模块中的函数和对象通常也带有一个下划线,并且可以由两个。

但是__double_underscore名称在模块中不是 name-mangled,例如。 系统不会导入名称以一个( 或者更多) 下划线开头中发生的情况是,如果你从一个模块中导入所有( 从模块导入 *),的名称shown.也不重要

下面是关于双下划线属性如何影响继承类的简单说明示例。 因此,使用以下设置:


class parent(object):
 __default ="parent"
 def __init__(self, name=None):
 self.default = name or self.__default

 @property
 def default(self):
 return self.__default

 @default.setter
 def default(self, value):
 self.__default = value


class child(parent):
 __default ="child"

如果在 python REPL中创建一个子实例,你将看到下面的内容


child_a = child()
child_a.default # 'parent'
child_a._child__default # 'child'
child_a._parent__default # 'parent'

child_b = child("orphan")
## this will show 
child_b.default # 'orphan'
child_a._child__default # 'child'
child_a._parent__default # 'orphan'

某些情况下这可能很明显,但它在一个更复杂的环境中让我措手不及

...