对象名称前的单下划线和双下划线是什么意思?
- 2024-11-18 08:40:00
- admin 原创
- 15
问题描述:
在 Python 中,对象名称前的单下划线和双下划线代表什么?
解决方案 1:
单下划线
在类中,以下划线开头的名称向其他程序员表明该属性或方法旨在在该类内部使用。但是,隐私不会以任何方式强制执行。在模块中对函数使用前导下划线表示不应从其他地方导入该函数。
来自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!'}
解决方案 2:
_foo
:仅是一种约定俗成。程序员用来表明变量是私有变量(无论在 Python 中是什么意思)的一种方式。__foo
:这具有实际意义。解释器用 替换此名称,_classname__foo
以确保该名称不会与另一个类中的类似名称重叠。__foo__
:仅是一种约定。一种让 Python 系统使用不会与用户名冲突的名称的方法。
在 Python 世界中,其他形式的下划线都没有任何意义。此外,在这些约定中,类、变量、全局等之间没有区别。
解决方案 3:
到目前为止,答案都很棒,但缺少一些细节。单个前导下划线不仅仅是一种惯例:如果您使用from foobar import *
,并且模块foobar
未定义__all__
列表,则从模块导入的名称不包括带有前导下划线的名称。我们假设它主要是一种惯例,因为这种情况是一个非常模糊的角落;-)。
前导下划线约定不仅广泛用于私有名称,也用于 C++ 所称的受保护名称 - 例如,完全旨在由子类覆盖的方法名称(甚至必须被覆盖,因为它们在基类中raise NotImplementedError
!-)通常是单前导下划线名称,以向使用该类(或子类)实例的代码指示所述方法不应直接调用。
例如,为了创建一个具有不同于 FIFO 的排队规则的线程安全队列,需要导入 Queue、子类化 Queue.Queue,并覆盖诸如 和_get
之类的方法_put
;“客户端代码”从不调用那些(“钩子”)方法,而是调用(“组织”)公共方法,例如put
和get
(这被称为模板方法设计模式 - 例如,请参见此处,了解基于我关于该主题的演讲视频的有趣演示,并附加了演讲记录的摘要)。
编辑:演讲描述中的视频链接现已损坏。您可以在此处和此处找到前两个视频。
解决方案 4:
._variable
是半私人的,仅用于会议
.__variable
经常被错误地认为是超级私有的,而其实际含义只是为了防止意外访问而进行名称修改[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
而不是仅仅访问双下划线名称,您可以访问隐藏的值
解决方案 5:
以单下划线开头:
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
正如我们所见,B.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
更多细节,请参阅PEP-8 指南。更多魔法方法,请参阅此 PDF。
解决方案 6:
根据Python 中下划线的含义
单前导下划线(
_var
):命名约定表示名称仅供内部使用。通常 Python 解释器不强制执行(通配符导入除外),仅作为对程序员的提示。单尾下划线(
var_
):按照惯例使用,以避免与 Python 关键字的命名冲突。双前导下划线(
__var
):在类上下文中使用时触发名称修改。由 Python 解释器强制执行。双前导和尾随下划线(
__var__
):表示 Python 语言定义的特殊方法。请避免对您自己的属性使用这种命名方案。单下划线(
_
):有时用作临时或不重要的变量的名称(“不关心”)。还有:Python REPL中最后一个表达式的结果。
解决方案 7:
由于很多人都在参考雷蒙德的演讲,我只想把他说的话写下来,让大家更容易理解:
双下划线的用意不是为了隐私。其用意正是如此
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
在 中保留 的本地引用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
。
解决方案 8:
有时,你会看到一个带有下划线的元组,例如
def foo(bar):
return _('my_' + bar)
在这种情况下,_() 是本地化函数的别名,该函数根据语言环境对文本进行操作,将其转换为适当的语言等。例如,Sphinx 就是这样做的,您会在导入中找到
from sphinx.locale import l_, _
在sphinx.locale中,_()被指定为某些本地化函数的别名。
解决方案 9:
_var
:python 中以单下划线开头的变量是经典变量,用于告知使用您代码的其他人该变量应保留供内部使用。它们与经典变量有一点不同:在对定义它们的对象/模块进行通配符导入时不会导入它们(定义变量时除外__all__
)。例如:
# foo.py
var = "var"
_var = "_var"
# bar.py
from foo import *
print(dir()) # list of defined objects, contains 'var' but not '_var'
print(var) # var
print(_var) # NameError: name '_var' is not defined
_
:单下划线是前导单下划线变量的一个特例。它通常用作垃圾变量,用于存储以后不打算访问的值。它也不会通过通配符导入来导入。例如:此for
循环打印“我不能在课堂上说话”10 次,并且永远不需要访问该_
变量。
for _ in range(10):
print("I must not talk in class")
var_
:单尾下划线变量。它们是约定俗成的经典变量,可避免与 Python 关键字冲突。例如:
class_ = "MyClassName"
__var
:双前下划线变量(至少两个前下划线,最多一个尾部下划线)。当用作类属性(变量和方法)时,这些变量易受名称改编:在类之外,python 会将属性重命名为_<Class_name>__<attribute_name>
。示例:
class MyClass:
__an_attribute = "attribute_value"
my_class = MyClass()
print(my_class._MyClass__an_attribute) # "attribute_value"
print(my_class.__an_attribute) # AttributeError: 'MyClass' object has no attribute '__an_attribute'
当用作类外的变量时,它们的行为类似于单前导下划线变量。
__var__
:双下划线变量(至少两个前导和尾随下划线)。也称为dunders。python 使用此命名约定在内部定义变量。避免使用此约定以防止 python 更新时可能出现的名称冲突。双下划线变量的行为类似于单前导下划线变量:在类内部使用时不受名称修改的影响,但不会在通配符导入中导入。
解决方案 10:
如果真的想让变量只读,我认为最好的方法是使用 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 问了一个稍微不同的问题,但是因为我发现另一个问题询问“如何设置私有变量”与这个问题重复,所以我想在这里添加这个额外的信息。
解决方案 11:
很棒的答案,全部都是正确的。我提供了简单的例子以及简单的定义/含义。
意义:
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)
解决方案 12:
这是一个简单的示例,说明双下划线属性如何影响继承的类。因此,使用以下设置:
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'
对于某些人来说,这可能是显而易见的,但在更为复杂的环境中,这让我措手不及。
解决方案 13:
单下划线开头是一种惯例。从解释器的角度来看,名称是否以单下划线开头没有区别。
双前导和尾随下划线用于内置方法,例如__init__
、__bool__
等。
双前下划线无尾下划线也是一种惯例,但是,类方法将被解释器破坏。对于变量或基本函数名称,不存在任何差异。
解决方案 14:
你的问题很好,这不仅仅是关于方法。模块中的函数和对象通常也以一个下划线为前缀,也可以以两个下划线为前缀。
但是,例如,模块中的 __double_underscore 名称不会发生名称混乱。如果从模块 (from module import *) 导入所有名称,则不会导入以一个 (或多个) 下划线开头的名称,help(module) 中也不会显示这些名称。
解决方案 15:
Python 中不存在“私有”实例变量,即除了从对象内部之外无法访问的变量。但是,大多数 Python 代码都遵循一个惯例:带有下划线前缀的名称(例如 _spam)应被视为 API 的非公共部分(无论它是函数、方法还是数据成员)。它应被视为实现细节,如有更改,恕不另行通知。
参考
https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
解决方案 16:
了解 和 _ 的事实相当容易;其他答案都表达得相当好。用法则更难确定。
我的看法是这样的:
_
应该用来表明某个函数不是供公众使用的,例如 API。这和导入限制使其行为internal
与 c# 非常相似。
__
应该用来避免继承层次结构中的名称冲突并避免后期绑定。与 c# 中的 private 非常相似。
==>
如果你想表明某物不用于公共用途,但它应该表现得像protected
use _
。 如果你想表明某物不用于公共用途,但它应该表现得像private
use __
。
这也是我很喜欢的一句话:
问题在于,类的作者可能会合理地认为“此属性/方法名称应为私有的,只能从此类定义内部访问”,并使用 __private 约定。但后来,该类的用户可能会创建一个合法需要访问该名称的子类。因此,要么必须修改超类(这可能很困难或不可能),要么子类代码必须使用手动修改的名称(这充其量是丑陋和脆弱的)。
但我认为问题在于,如果没有 IDE 在您覆盖方法时发出警告,如果您意外地覆盖了基类中的方法,那么查找错误可能需要一段时间。
解决方案 17:
对于方法,您可以使用双下划线来隐藏私有“方法”,模式如下:
# Private methods of MyClass
def _MyClass__do_something(obj:'MyClass'):
print('_MyClass__do_something() called. type(obj) = {}'.format(type(obj)))
class MyClass():
def __init__(self):
__do_something(self)
mc = MyClass()
输出:
_MyClass__do_something() called. type(obj) = <class '__main__.MyClass'>
今天,当我尝试对类方法使用双下划线并出现NameError: name '_<class><method>' is not defined
错误时,我偶然发现了这个问题。
解决方案 18:
简单来说,让我们将 Python 变量的可访问性约定与 Java 中的访问修饰符进行比较:
(Python) = (Java)
_single_underscore_variable = Protected (Accessible to class and its subclasses)
__double_underscore_variable = Private (Accessible to class itself only)
no_underscore_variable = Public (Accessible anywhere)
参考:https://www.tutorialsteacher.com/python/public-private-protected-modifiers
解决方案 19:
_obj 表示受保护(可以在定义类及其子类中访问)
--obj 表示私有(它们不能从定义它们的类的外部直接访问。但是,Python 并不像其他一些语言那样强制执行严格的私有访问;相反,它执行名称修改以使访问更加困难但并非不可能。)
Mangling 是 Python 中的一项功能,可修改类成员的名称,使其更难从类外部直接访问。这是一种用于创建“伪私有”变量和方法的技术。这是通过在成员名称前加上双下划线 (__) 来实现的,但不以另一个双下划线结尾
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件