Ram*_*hum 1205 python naming-conventions
有人可以解释在Python中对象名称之前有前导下划线的确切含义吗?另外,解释单个和双重前导下划线之间的区别.此外,无论所讨论的对象是变量,函数,方法等,这个含义是否保持不变?
And*_*ton 1083
具有前导下划线的类中的名称只是向其他程序员指示该属性或方法是私有的.但是,名称本身并没有什么特别之处.
引用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!'}
Run Code Online (Sandbox Code Playgroud)
Ale*_*lli 294
到目前为止优秀的答案,但一些花絮丢失.单个前导下划线并不仅仅是一个约定:如果使用from foobar import *
,并且模块foobar
未定义__all__
列表,则从模块导入的名称不包括具有前导下划线的名称.让我们说它主要是一种惯例,因为这个案例是一个非常模糊的角落;-).
前导下划线约定不仅被广泛用于私有名称,而且被广泛用于C++将其称为受保护的名称 - 例如,完全打算被子类覆盖的方法的名称(甚至是那些必须被覆盖的方法的名称)它们的基类raise NotImplementedError
! - )通常是单引导下划线名称,用于指示代码使用该类(或子类)的实例,这些方法不是直接调用的.
例如,要创建一个具有与FIFO不同的排队规则的线程安全队列,可以导入Queue,子类Queue.Queue,并覆盖这样的方法_get
和_put
; "客户端代码"从不调用那些("hook")方法,而是调用("组织")公共方法,例如put
和get
(这被称为模板方法设计模式 - 请参阅此处以获取基于视频的有趣演示文稿关于这个问题的我的谈话,增加了成绩单的概要).
Ned*_*der 281
__foo__
:这只是一种约定,是Python系统使用不会与用户名冲突的名称的一种方式.
_foo
:这只是一种约定,是程序员指示变量是私有的一种方式(无论在Python中是什么意思).
__foo
:这具有实际意义:解释器将此名称替换_classname__foo
为确保名称不会与另一个类中的类似名称重叠的方式.
没有其他形式的下划线在Python世界中有意义.
这些约定中的类,变量,全局等没有区别.
Nic*_*CSE 194
._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'
Run Code Online (Sandbox Code Playgroud)
t._b是可访问的,因为它只是按惯例隐藏的
>>> t.__a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'
Run Code Online (Sandbox Code Playgroud)
找不到t .__ a,因为它因名称错误而不再存在
>>> t._Test__a
'a'
Run Code Online (Sandbox Code Playgroud)
通过访问instance._className__variable
而不仅仅是双下划线名称,您可以访问隐藏值
Pyt*_*Dev 109
开头的单下划线:
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)
Run Code Online (Sandbox Code Playgroud)
(此代码段取自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!
Run Code Online (Sandbox Code Playgroud)
输出:
$ python test.py
I'm test method in class A
I'm test method in class A
Run Code Online (Sandbox Code Playgroud)
现在创建一个子类B并为__test方法进行自定义
class B(A):
def __test(self):
print "I'm test method in class B"
b = B()
b.test()
Run Code Online (Sandbox Code Playgroud)
输出将是....
$ python test.py
I'm test method in class A
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,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
Run Code Online (Sandbox Code Playgroud)
总有一个操作符或本机函数调用这些魔术方法.有时它只是在特定情况下的钩子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
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅PEP-8指南.有关更多魔术方法,请参阅此PDF.
Feu*_*uda 29
_var
):指示名称的命名约定供内部使用。通常不被 Python 解释器强制执行(通配符导入除外),仅作为对程序员的提示。var_
):按照惯例使用以避免与 Python 关键字的命名冲突。__var
):在类上下文中使用时触发名称重整。由 Python 解释器强制执行。__var__
):表示 Python 语言定义的特殊方法。避免为您自己的属性使用这种命名方案。_
):有时用作临时或无关紧要的变量的名称(“不关心”)。另外:Python REPL 中最后一个表达式的结果。Tim*_*m D 18
有时你会看到一个带有前导下划线的元组
def foo(bar):
return _('my_' + bar)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,正在发生的是_()是本地化函数的别名,该函数根据语言环境对文本进行操作以将其置于适当的语言等.例如,Sphinx会这样做,你可以在导入中找到它
from sphinx.locale import l_, _
Run Code Online (Sandbox Code Playgroud)
在sphinx.locale中,_()被指定为某些本地化函数的别名.
如果一个人真的想让变量只读,恕我直言,最好的方法是使用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)
Run Code Online (Sandbox Code Playgroud)
我知道OP提出了一个不同的问题但是因为我发现了另一个问题,询问"如何设置私有变量"标记重复这个问题,我想在这里添加这些额外的信息.
很好的答案,都是正确的。我提供了简单的例子以及简单的定义/含义。
意义:
some_variable --?这是公开的,任何人都可以看到。
_some_variable --?它是公开的,任何人都可以看到,但这是表明私有的约定……警告Python 没有执行任何强制措施。
__some_variable --?Python 用 _classname__some_varaible (AKA name mangling) 替换变量名,它减少/隐藏它的可见性,更像是私有变量。
老实说,根据 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)
Run Code Online (Sandbox Code Playgroud)
这是一个关于双下划线属性如何影响继承类的简单说明性示例。因此,使用以下设置:
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"
Run Code Online (Sandbox Code Playgroud)
如果然后在 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'
Run Code Online (Sandbox Code Playgroud)
这对某些人来说可能很明显,但它让我在更复杂的环境中措手不及
既然有这么多人在谈论雷蒙德的演讲,我将写下他的话变得更容易一些:
双下划线的目的不是关于隐私。目的是像这样完全使用它
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
双下划线具有名称修饰,因此父类中的本地引用在派生类中被覆盖的可能性很小。因此“ 使子类可以自由地覆盖任何一种方法而不会破坏其他方法 ”。
如果您的类不会被继承,或者方法重写不会破坏任何内容,那么您根本不需要__double_leading_underscore
。
_var
:python 中带前导单下划线的变量是经典变量,旨在通知使用您的代码的其他人该变量应保留供内部使用。它们与经典变量有一点不同:在定义它们的对象/模块的通配符导入时,它们不会被导入(定义__all__
变量时例外)。例如:
# foo.py
var = "var"
_var = "_var"
Run Code Online (Sandbox Code Playgroud)
# 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
Run Code Online (Sandbox Code Playgroud)
_
:单下划线是前导单下划线变量的特例。按照惯例,它被用作垃圾变量,以存储以后不打算访问的值。它也不是通过通配符导入导入的。例如:这个for
循环打印了 10 次“我不能在课堂上说话”,并且永远不需要访问_
变量。
for _ in range(10):
print("I must not talk in class")
Run Code Online (Sandbox Code Playgroud)
__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'
Run Code Online (Sandbox Code Playgroud)
当用作类外的变量时,它们的行为类似于单个前导下划线变量。
__var__
: 双前导和尾随下划线变量(至少两个前导和尾随下划线)。也称为dunders。python 使用此命名约定在内部定义变量。避免使用此约定来防止 Python 更新可能出现的名称冲突。Dunder 变量的行为类似于单个前导下划线变量:它们在类中使用时不受名称修改的影响,但不会在通配符导入中导入。
归档时间: |
|
查看次数: |
361814 次 |
最近记录: |