关于python:对象名之前的单下划线和双下划线的含义是什么?

What is the meaning of a single and a double underscore before an object name?

有人能解释一下在python中,在对象名称之前加上前导下划线的确切含义吗?另外,解释单下划线和双下划线之间的区别。此外,这是否意味着所讨论的对象是变量、函数、方法等保持不变?


单下划线

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

引用PET-8:

_single_leading_underscore: weak"internal use" indicator. E.g. from M import * does not import objects whose name starts with an underscore.

双下划线(名称混乱)

从python文档中:

Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, so it can be used to define class-private instance and class variables, methods, variables stored in globals, and even variables stored in instances. private to this class on instances of other classes.

以及来自同一页的警告:

Name mangling is intended to give classes an easy way to define"private" instance variables and methods, without having to worry about instance variables defined by derived classes, or mucking with instance variables by code outside the class. Note that the mangling rules are designed mostly to avoid accidents; it still is possible for a determined soul to access or modify a variable that is considered private.

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 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++所称的受保护的类——例如,完全被子类重写的方法的名称(即使是在基类EDCOX1×9)中必须重写的方法的名称。-)通常是一个前导下划线名称,用于指示代码使用该类(或子类)的实例,这些实例并不打算直接调用所述方法。

例如,为了使一个线程安全队列具有与FIFO不同的排队规则,一个导入队列、子类queue.queue,并重写诸如_get_put这样的方法;"客户机代码"从不调用那些("钩子")方法,而是调用诸如putget这样的("组织")公共方法(这被称为作为模板方法设计模式——例如,这里有一个有趣的演示,基于我关于这个主题的谈话视频,并添加了抄本的概要)。


__foo__:这只是一种约定,是Python系统使用不会与用户名冲突的名称的一种方法。

_foo:这只是一种约定,程序员可以用它来表示变量是私有的(在python中是什么意思)。

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

在Python世界中,任何其他形式的下划线都没有意义。

在这些约定中,类、变量、全局等之间没有区别。


._variable是半私有的,仅用于约定。

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

.__variable__通常是为内置方法或变量保留的。

如果您非常想访问.__mangled变量,您仍然可以访问这些变量。双下划线仅用于将变量命名为诸如instance._className__mangled之类的变量。

例子:

1
2
3
4
5
6
7
8
class Test(object):
    def __init__(self):
        self.__a = 'a'
        self._b = 'b'

>>> t = Test()
>>> t._b
'b'

T."B是可以接近的,因为它只被惯例所隐藏。

1
2
3
4
>>> t.__a
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'

T."找不到A,因为它由于名称管理而不再存在。"

1
2
>>> t._Test__a
'a'

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


开头有一个下划线:

python没有真正的私有方法。相反,在方法或属性名称的开头加上一个下划线意味着您不应该访问此方法,因为它不是API的一部分。

1
2
3
4
5
6
7
8
9
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)。在这段代码中,errors是公共属性,但此属性调用的方法"get-u-errors"是"private",因此您不应该访问它。

开头有两个下划线:

这引起了很多混乱。它不应该用于创建私有方法。应该使用它来避免您的方法被子类重写或被意外访问。让我们看一个例子:

1
2
3
4
5
6
7
8
9
10
11
class A(object):
    def __test(self):
        print"I'm a test method in class A"

    def test(self):
        self.__test()

a = A()
a.test()
# a.__test() # This fails with an AttributeError
a._A__test() # Works! We can access the mangled name directly!

输出:

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

现在创建一个子类B并为uuu测试方法进行定制

1
2
3
4
5
6
class B(A):
    def __test(self):
        print"I'm test method in class B"

b = B()
b.test()

输出将是……

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

正如我们所看到的,a.test()没有像我们预期的那样调用b.u test()方法。但事实上,这是正确的行为。两个名为uu test()的方法被自动重命名(损坏)为a_u test()和b_u test(),因此它们不会意外重写。当您创建一个以开头的方法时,这意味着您不希望任何人能够覆盖它,并且您只打算从自己的类内部访问它。

开头和结尾有两个下划线:

当我们看到像__this__这样的方法时,不要调用它。这是一个Python要调用的方法,而不是您。让我们看看:

1
2
3
4
5
6
7
8
9
10
11
>>> name ="test string"
>>> name.__len__()
11
>>> len(name)
11

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

总是有一个运算符或本机函数调用这些神奇的方法。有时在特定情况下,它只是一个钩子python调用。例如,当调用__new__()来构建实例后创建对象时,调用__init__()

让我们举个例子…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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指南。有关更多魔术方法,请参阅此PDF。


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

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

在这种情况下,发生的事情是u()是一个本地化函数的别名,该函数对文本进行操作,以根据区域设置将其放入正确的语言等。例如,斯芬克斯就是这样做的,你可以在进口产品中找到

1
from sphinx.locale import l_, _

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


如果真的想使变量只读,imho最好的方法是使用property(),只传递getter。使用property(),我们可以完全控制数据。

1
2
3
4
5
6
7
8
9
10
11
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__等。

不带尾随对等项的双前导下划线也是一种约定,但是类方法将被解释器破坏。对于变量或基本函数名,不存在差异。


"private"实例变量只能从对象内部访问,但在python中不存在。但是,大多数python代码后面都有一个约定:以下划线(例如"spam")作为前缀的名称应被视为API的非公共部分(无论它是函数、方法还是数据成员)。应将其视为实施细节,如有更改,恕不另行通知。

参考https://docs.python.org/2/tutorial/classes.html私有变量和类本地引用


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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中创建一个子实例,您将看到下面的内容

1
2
3
4
5
6
7
8
9
10
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'

这对一些人来说可能是显而易见的,但它让我在一个更复杂的环境中措手不及。


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

但是,例如,双下划线的名称不会在模块中损坏。如果从模块(从模块导入*)导入所有内容,则不会导入以一个(或多个)下划线开头的名称,也不会导入帮助(模块)中显示的名称。


答案很好,都是正确的。我提供了简单的例子以及简单的定义/含义。

含义:

一些变量--?这是公开的,任何人都能看到。

_一些变量--?这是公开的,任何人都可以看到,但这是一种表示私有的约定……警告:Python不执行任何强制操作。

_一些人…?python将变量名替换为classname_uusome_varaible(又名mangling),它减少/隐藏了它的可见性,更像是私有变量。

老实说,根据python文档

""Private" instance variables that cannot be accessed except from
inside an object don’t exist in Python"

例子:

1
2
3
4
5
6
7
8
9
10
11
class A():
    here="abc"
    _here="_abc"
    __here="__abc"


aObject=A()
print(aObject.here)
print(aObject._here)
# now if we try to print __here then it will fail because it's not public variable
#print(aObject.__here)

既然有那么多人提到雷蒙德的话,我只想写下他说的话,让事情变得简单一点:

The intention of the double underscores was not about privacy. The intention was to use it exactly like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Circle(object):

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

    def area(self):
        p = self.__perimeter()
        r = p / math.pi / 2.0
        return math.pi * r ** 2.0

    def perimeter(self):
        return 2.0 * math.pi * self.radius

    __perimeter = perimeter  # local reference


class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

It's actually the opposite of privacy, it's all about freedom. It makes your subclasses free to override any one method without breaking the others.

假设你在Circle中没有保留perimeter的本地引用。现在,派生类Tire覆盖perimeter的实现,而不涉及area。当你调用Tire(5).area()时,理论上它应该仍然使用Circle.perimeter进行计算,但实际上它使用的是Tire.perimeter这不是预期的行为。这就是为什么我们需要一个本地的循环参考。

但是为什么用__perimeter代替_perimeter?因为_perimeter仍然给派生类机会重写:

1
2
3
4
5
6
class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

    _perimeter = perimeter

双下划线的名称有误,所以父类中的本地引用在派生类中被重写的可能性很小。因此,"使您的子类可以在不破坏其他方法的情况下自由地重写任何一个方法"。

如果您的类不会被继承,或者方法重写不会破坏任何内容,那么您就不需要__double_leading_underscore


了解uuuuuuuuuu和uuuuu的事实非常容易;其他答案也很好地表达了它们。这种用法很难确定。

我就是这样看的:

1
_

应用于指示函数不作为公共用途,例如API。这和进口限制使得它的行为与c中的internal非常相似。

1
__

应用于避免继承层次结构中的名称冲突,并避免后期绑定。很像C里的私人。

=>

如果你想指出某件东西不是供公共使用的,但它应该像protected使用_。如果你想指出某件东西不是供公共使用的,但它应该像private使用__

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

The problem is that the author of a class may legitimately think"this
attribute/method name should be private, only accessible from within
this class definition" and use the __private convention. But later on,
a user of that class may make a subclass that legitimately needs
access to that name. So either the superclass has to be modified
(which may be difficult or impossible), or the subclass code has to
use manually mangled names (which is ugly and fragile at best).

但问题在于,我认为,如果没有IDE在重写方法时警告您,那么在意外地重写基类中的方法时,查找错误可能需要一段时间。