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 , whereclassname 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!'} |
到目前为止答案很好,但有些小道消息不见了。单个前导下划线不仅仅是一种约定:如果使用
领先的下划线约定不仅被广泛用于私有名,而且还用于C++所称的受保护的类——例如,完全被子类重写的方法的名称(即使是在基类EDCOX1×9)中必须重写的方法的名称。-)通常是一个前导下划线名称,用于指示代码使用该类(或子类)的实例,这些实例并不打算直接调用所述方法。
例如,为了使一个线程安全队列具有与FIFO不同的排队规则,一个导入队列、子类queue.queue,并重写诸如
在Python世界中,任何其他形式的下划线都没有意义。
在这些约定中,类、变量、全局等之间没有区别。
如果您非常想访问
例子:
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' |
通过访问
开头有一个下划线:
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)。在这段代码中,
开头有两个下划线:
这引起了很多混乱。它不应该用于创建私有方法。应该使用它来避免您的方法被子类重写或被意外访问。让我们看一个例子:
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(),因此它们不会意外重写。当您创建一个以开头的方法时,这意味着您不希望任何人能够覆盖它,并且您只打算从自己的类内部访问它。
开头和结尾有两个下划线:
当我们看到像
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调用。例如,当调用
让我们举个例子…
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问了一个有点不同的问题,但是由于我发现了另一个问题,问"如何设置私有变量",这个问题被标记为重复,所以我想在这里添加这个额外的信息。
单前导下划线是一种惯例。如果名称以单个下划线开头,则与解释器的观点没有区别。
内置方法使用双前导和尾随下划线,如
不带尾随对等项的双前导下划线也是一种约定,但是类方法将被解释器破坏。对于变量或基本函数名,不存在差异。
"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.25It'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.
假设你在
但是为什么用
1 2 3 4 5 6 | class Tire(Circle): def perimeter(self): return Circle.perimeter(self) * 1.25 _perimeter = perimeter |
双下划线的名称有误,所以父类中的本地引用在派生类中被重写的可能性很小。因此,"使您的子类可以在不破坏其他方法的情况下自由地重写任何一个方法"。
如果您的类不会被继承,或者方法重写不会破坏任何内容,那么您就不需要
了解uuuuuuuuuu和uuuuu的事实非常容易;其他答案也很好地表达了它们。这种用法很难确定。
我就是这样看的:
1 | _ |
应用于指示函数不作为公共用途,例如API。这和进口限制使得它的行为与c中的
1 | __ |
应用于避免继承层次结构中的名称冲突,并避免后期绑定。很像C里的私人。
=>
如果你想指出某件东西不是供公共使用的,但它应该像
这也是我非常喜欢的一句话:
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在重写方法时警告您,那么在意外地重写基类中的方法时,查找错误可能需要一段时间。