有人可以解释一下在Python中对象名称前加下划线的确切含义,以及两者之间的区别吗?
此外,无论所讨论的对象是变量,函数,方法等,该含义是否都保持不变?
有人可以解释一下在Python中对象名称前加下划线的确切含义,以及两者之间的区别吗?
此外,无论所讨论的对象是变量,函数,方法等,该含义是否都保持不变?
Answers:
类中带有下划线的名称仅是为了向其他程序员表明该属性或方法旨在私有。但是,名称本身并没有做任何特别的事情。
引用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!'}
__
双下划线作为变量名是什么意思?像a, __ = foo()
到目前为止,很好的答案,但缺少一些花絮。单个前导下划线并不仅仅是一个约定:如果使用from foobar import *
,并且模块foobar
未定义__all__
列表,则从模块导入的名称将不包括带有下划线的名称。可以说这主要是一个约定,因为这种情况是一个相当模糊的角落;-)。
领先的下划线惯例被广泛用于不只是私人的名字,而且对什么C ++称之为保护的-例如,都完全的方法名称由子类(甚至是那些覆盖有被否决,因为在它们的基类raise NotImplementedError
!-)通常是单引号下划线名称,用于指示使用该类(或子类)的实例进行编码时,这些方法并不旨在被直接调用。
例如,要创建一个与FIFO不同的排队规则的线程安全队列,可以导入Queue,将Queue.Queue子类化,并覆盖诸如_get
和_put
;之类的方法。“客户端代码”从不调用那些(“挂钩”)方法,而是调用(和“组织”)公共方法,例如put
和get
(称为模板方法设计模式),例如,有关基于视频的有趣演示,请参见此处。我关于这个话题的演讲,以及抄本的提要)。
_var_name
还是使用var_name
+从中排除呢__all__
?
__all__
每当您想使模块from spam import *
友好时(包括在交互式解释器处),请使用显式。因此,在大多数情况下,答案是两者兼而有之。
_
私人时,我不喜欢它。显然,我在谈论类比,因为Python中没有什么是真正私有的。当深入到语义我想说,我们可以绑_
到Java的保护,因为proctected在Java中的意思是“派生类和/或在相同的包”。用模块替换软件包,因为PEP8已经告诉我们_
在谈论*
导入时这不仅仅是一个约定,而且您已经有了。当谈论一个类中的标识符时,这绝对__
等同于Java的私有。
__foo__
:这只是一个约定,这是Python系统使用不会与用户名冲突的名称的一种方式。
_foo
:这只是一个约定,是程序员用来表明变量是私有的(无论在Python中是什么意思)的一种方式。
__foo
:这具有实际含义:解释器将其替换为,_classname__foo
以确保该名称不会与另一个类中的相似名称重叠。
在Python世界中,没有其他形式的下划线具有意义。
在这些约定中,类,变量,全局等之间没有区别。
__foo
而好奇。它如何与其他类的相似方法名称重叠?我的意思是您仍然必须以类似的方式访问它instance.__foo()
(如果未由解释程序重命名),对吗?
B
子类class A
,并且都实现foo()
,则B.foo()
覆盖从.foo()
继承A
。的实例B
只能通过进行访问B.foo()
,除非通过super(B).foo()
。
._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的一个钩子调用。例如__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
双下划线的目的不是关于隐私。目的是像这样完全使用它
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
实际上,这是隐私的对立面,它与自由有关。它使您的子类可以随意覆盖任何一种方法而不会破坏其他方法。
假设您没有保留perimeter
in 的本地引用Circle
。现在,派生类将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
。
进入https://dbader.org/blog/含义-underscores-in-python
好的答案都正确。我提供了简单的示例以及简单的定义/含义。
含义:
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)
您的问题很好,不仅是方法。模块中的函数和对象通常也以一个下划线为前缀,并且可以以两个为前缀。
但是,例如,__double_underscore名称在模块中没有名称混杂。发生的情况是,如果您从模块(从module import *)全部导入,则不会导入以一个(或多个)下划线开头的名称,也不会在help(module)中显示名称。
这是一个关于双下划线属性如何影响继承的类的简单说明性示例。因此,使用以下设置:
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'
这对于某些人可能是显而易见的,但是在更复杂的环境中使我措手不及
Python中不存在只能从对象内部访问的“私有”实例变量。但是,大多数Python代码遵循一个约定:以下划线开头的名称(例如_spam)应被视为API的非公开部分(无论是函数,方法还是数据成员) 。它应被视为实现细节,如有更改,恕不另行通知。
参考 https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
得到_和__的事实非常容易。其他答案很好地表达了他们。使用情况很难确定。
这是我的看法:
_
应该用于表示某个功能不公开使用,例如API。这和导入限制使它的行为很像internal
在c#中。
__
应该用来避免继承层次结构中的名称冲突以及避免后期绑定。就像C#中的private。
==>
如果您想指出某事不是公共用途,而是应该像protected
use 一样_
。如果您想指出某事不是公共用途,而是应该像private
use 一样__
。
这也是我非常喜欢的报价:
问题在于,类的作者可能会合理地认为“此属性/方法名称应为私有的,只能从该类定义中访问”,并使用__private约定。但是稍后,该类的用户可能会创建合法需要访问该名称的子类。因此,要么必须修改超类(可能很难或不可能),要么子类代码必须使用手动修改的名称(充其量是丑陋和脆弱的)。
但是我的问题是,如果没有IDE在您重写方法时警告您,如果您意外地从基类中重写了一个方法,则发现错误可能会花费您一段时间。