对象名称前的单下划线和双下划线是什么意思?

有人可以解释一下在 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!'}

到目前为止,很好的答案,但缺少一些花絮。单个前导下划线并不仅仅是一个约定:如果from foobar import * ,并且模块foobar没有定义__all__列表,则从模块导入的名称将包括带有下划线的名称。可以说这主要是一个约定,因为这种情况是一个不太明显的角落;-)。

领先的下划线惯例被广泛用于不只是私人的名字,而且对什么 C ++ 称之为保护的 - 例如,都完全的方法名称由子类(甚至是那些必须重写,因为在被覆盖它们raise NotImplementedError !-)的基类通常是单raise NotImplementedError下划线名称,以指示使用该类(或子类)的实例进行编码,表示该方法不应直接调用。

例如,要创建一个与 FIFO 不同的排队规则的线程安全队列,可以导入 Queue,将 Queue.Queue 子类化,并覆盖_get_put等方法; “客户端代码” 从未称那些(“钩子”)的方法,而是(“安排”)的公共方法,如putget (这就是所谓的模板方法设计模式 - 例如参见这里基于一个有趣的演示一段关于我的话题的视频,并附带抄本的摘要)。

编辑:演讲说明中的视频链接现在已断开。您可以在此处此处找到前两个视频。

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

_foo :这只是一个约定,程序员可以通过这种方式表明该变量是私有的(无论在 Python 中是什么意思)。

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

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

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

._variable._variable的,仅用于约定

.__variable通常被错误地认为是超私有的,而其实际含义只是为了防止意外访问而使用 namemangle [1]

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

如果您非常需要,仍可以访问.__mangled变量。双下划线仅将变量命名或将变量重命名为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,因为由于名称修改而不再存在

>>> 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)。在此代码中, errors是一个公共属性,但是此属性调用的方法_get_errors 是 “私有” 的,因此您不应访问它。

一开始有两个下划线:

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

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!

输出:

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

现在创建一个子类 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()方法。但实际上,这是__的正确行为。这两个称为__test()的方法会自动重命名(混合)为_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 的一个钩子调用。例如,在调用__new__()生成实例后创建对象时调用__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 指南 。有关更多魔术方法,请参见此PDF

有时您看起来像是一个带下划线的元组,如下所示

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

在这种情况下,正在发生的情况是_()是本地化功能的别名,该功能基于文本在文本上进行操作以将其放入适当的语言等。例如,Sphinx 执行此操作,您将在导入文件中找到

from sphinx.locale import l_, _

在 sphinx.locale 中,_()被分配为某些本地化功能的别名。

如果您真的想将变量设为只读,恕我直言,最好的方法是使用仅传递有 getter 的 property()。使用 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 提出了一个稍有不同的问题,但是由于我发现了另一个问题,询问 “如何设置私有变量” 与此标记为重复,因此我想在此处添加此附加信息。

既然有这么多人在谈论雷蒙德的演讲 ,我将写下他的话变得更容易一些:

双下划线的目的不是关于隐私。目的是像这样完全使用它

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

实际上,这是隐私的对立面,它与自由有关。它使您的子类可以随意覆盖任何一种方法而不会破坏其他方法

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

但是,为什么用__perimeter而不是_perimeter呢?因为_perimeter仍然为派生类提供了覆盖的机会:

class Tire(Circle):

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

    _perimeter = perimeter

双下划线具有名称修饰,因此父类中的本地引用在派生类中被覆盖的可能性很小。因此 “ 使子类可以自由地覆盖任何一种方法而不会破坏其他方法 ”。

如果您的类不会被继承,或者方法重写不会破坏任何东西,那么您根本不需要__double_leading_underscore

单个下划线是惯例。名称是否以单个下划线开头,与解释器的观点没有区别。

内置方法使用双__bool__和尾部下划线,例如__init____init__ __bool__等。

双下划线开头 W / O 尾随同行是惯例也是如此,但是,该类方法将被错位的解释。对于变量或基本函数名称,没有区别。

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

含义:

some_variable--►任何人都可以看到这是公开的。

_some_variable--►任何人都可以看到这是公开的,但这是表示私有的约定... 警告 Python 不会执行任何强制执行。

__some_varaible--►Python 将变量名替换为_classname__some_varaible(又名名称改写),它降低 / 隐藏了它的可见性,更像是私有变量。

坦白地说, 根据 Python 文档

“Python 中不存在只能从对象内部访问的 “私有” 实例变量”

这个例子:

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)