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

我想彻底澄清这一点。 有人可以解释在Python中的对象的名称之前有下划线的确切含义吗? 还要解释一个单独的和一个双重的下划线之间的区别。 另外,不pipe这个对象是一个variables,一个函数,一个方法等,这个意义是否保持不变?

单下划线

在一个类中,带有前导下划线的名称只是向其他程序员指出该属性或方法是私有的。 然而,这个名字本身并没有什么特别之处。

引用PEP-8 :

_single_leading_underscore:弱的“内部使用”指标。 例如from M import *不导入名称以下划线开头的对象。

双下划线(Name Mangling)

从Python文档 :

任何forms的__spam标识符(至less两个前导下划线,最多一个尾随下划线)被文本replace为_classname__spam ,其中classname是当前类名,前导下划线被去除。 这种修改不考虑标识符的语法位置,因此可以用来定义类私有实例和类variables,方法,存储在全局variables中的variables,甚至是存储在实例中的variables。 对其他类的实例私有的这个类。

并从相同的页面警告:

Name mangling旨在为类提供一种简单的方法来定义“私有”实例variables和方法,而不必担心由派生类定义的实例variables,或者通过类之外的代码使用实例variables。 请注意,规则的devise主要是为了避免事故; 确定的灵魂仍然可以访问或修改被认为是私人的variables。

 >>> 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__列表,那么从模块导入的名称将包含带有前导下划线的名称。 让我们说这主要是一个惯例,因为这个案例是一个非常晦涩的angular落;-)。

前导 – 下划线约定不仅用于私有名称,而且还用于C ++所称的受保护对象 – 例如,完全打算由子类覆盖的方法的名称(即使是由于它们raise NotImplementedError的基类raise NotImplementedError ! – )通常是单raise NotImplementedError – 下划线名称,表示使用该类(或子类)的实例进行编码, 避免所述方法被直接调用。

例如,为了创build一个线程安全的队列,使用与FIFO不同的排队规则,可以导入Queue,Queue.Queue的子类,并覆盖_get_put类的方法; “客户端代码”从来不会调用那些(“钩子”)方法,而是(“组织化”)公共方法,比如putget (这就是所谓的模板方法devise模式 – 参见例如一个有趣的演示文稿,基于关于这个问题的一个关于我的谈话的video,以及对成绩单的总结)。

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

_foo :这只是一个惯例,是程序员指出variables是私有的(不pipe在Python中是什么意思)。

__foo :这具有真正的意义:解释器用_classname__fooreplace这个名字,以确保名称不会与另一个类中的类似名称重叠。

没有其他forms的下划线在Python世界中有意义。

这些惯例中的类,variables,全局等没有区别。

._variable._variable的,只是为了约定

.__variable经常被错误地认为是超私人的,而它的实际含义只是为了防止意外访问而命名[1]

.__variable__通常保留用于内置方法或variables

如果你非常想要的话,你仍然可以访问.__mangledvariables。 双下划线只是namemangles,或者重命名variables,像instance._className__mangled

例:

 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没有被find,因为名称不再存在

 >>> t._Test__a 'a' 

通过访问instance._className__variable而不是双下划线名称,您可以访问隐藏的值

开始时单下划线:

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) 

从django源代码(django / forms / forms.py)中提取代码片段。 这意味着错误是一个属性,它是模块的一部分,但是这个属性调用的方法_get_errors是“private”的,所以你不应该访问它。

开头两个下划线:

这造成了很多混乱。 它不应该被用来创build一个私人的方法。 它应该用来避免你的方法被子类重写或偶然访问。 我们来看一个例子:

 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 I'm test method in class A 

现在创build一个子类B并为__test方法进行自定义

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

输出将是…

 $ python test.py I'm test method in class A 

正如我们所见,A.test()并没有像我们所期望的那样调用B .__ test()方法。 但事实上,这是__的正确行为。 所以当你创build一个以__开头的方法时,这意味着你不想让任何人能够覆盖它,只能从自己的类中访问它。

两个下划线开头和结尾:

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

 >>> name = "test string" >>> name.__len__() 11 >>> len(name) 11 >>> number = 10 >>> number.__add__(40) 50 >>> number + 50 60 

总是有一个调用这些魔术方法的运算符或本地函数。 有时它只是一个钩子python在特定情况下调用。 例如,在调用__new__()以构build实例之后创build对象时,将调用__init__()

我们来举个例子

 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中find更多的魔术方法。 RafeKettler/magicmethods/blob/master/magicmethods.html

有时候你会看到像是一个带有前导下划线的元组

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

在这种情况下,发生的事情是_()是一个本地化函数的别名,它根据语言环境对文本进行操作以将其放入适当的语言中。 例如,狮身人面像这样做,你会发现在import

 from sphinx.locale import l_, _ 

而在sphinx.locale中,_()被指定为某个本地化函数的别名。

如果真的想做一个只读的variables,恕我直言,最好的办法是使用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问了一个不同的问题,但因为我发现另一个问题,要求“如何设置私有variables”与这个标记重复,我想在这里添加这个额外的信息。

单下划线是一个约定。 如果名字是否以一个下划线开始,与解释者的观点没有什么不同。

__bool__和尾部下划线用于内置方法,如__init____init__ __bool__

没有双引号的下划线也是一个惯例,但是类方法将会被解释器所破坏 。 对于variables或基本函数名称没有区别。

你的问题很好,不仅是方法。 模块中的函数和对象通常以一个下划线作为前缀,可以加前缀两个。

但是例如__double_underscore名称在模块中没有名称错位。 如果你从一个模块(从模块导入*)导入所有的名字,也不会导入以一个(或多个)下划线开头的名字,也不会显示在帮助(模块)中显示的名字。

Python中不存在“私有”实例variables,除了在对象中不能访问的variables。 但是,大多数Python代码都有一个约定:以下划线(例如_spam)作为前缀的名称应被视为API的非公开部分(无论是函数,方法还是数据成员) 。 这应该被视为实施细节,如有更改,恕不另行通知。

参考https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references

下面是一个关于双下划线属性如何影响inheritance类的简单示例。 所以采用以下设置:

 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中创build一个子实例,你会看到下面的代码

 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' 

这对一些人来说可能是显而易见的,但是在一个更加复杂的环境中,这让我无法控制

获取_和__的事实非常简单, 其他答案expression得很好。 用法很难确定。

这是我的看法:

 _ 

应该用来表示一个函数不是作为一个API来公开使用的。 这和import限制使得它在C#中的performance非常像internal

 __ 

应该用来避免在inheritancehirarchy名称冲突,并避免后缀。 就像在C#中的私人。

==>

如果你想表明某些东西不是供公众使用的,但它应该像protected使用一样。 如果你想表明一些东西不是供公众使用的,但它应该像private使用__

这也是我非常喜欢的一句话:

问题是,一个类的作者可能会合理地认为“这个属性/方法名称应该是私有的,只能从这个类定义中访问”,并使用__private约定。 但是稍后,该类的用户可能会创build一个合法需要访问该名称的子类。 所以要么修改超类(这可能是困难的或不可能的),要么子类代码必须使用手工修改的名字(最好是丑陋的和脆弱的)。

但问题是我认为,如果没有IDE在您覆盖方法时发出警告,那么如果您偶然从基类中覆盖了一个方法,则发现错误可能需要一段时间。