近日有同事忽然问我 关于python中私有变量的事 结果一扯呢 就扯到单双下划线了,我明明知道 但是在给他说的时候 忽然一下有点模糊了 搞得我怀疑自己智商在退步 于是乎 兴起 再来整理一番
-------------- 前 记
python中下环线共有5中 下面开始一一介绍
First Blood: 单前导下划线: _ xxx
Double Kill: 单末尾下划线: xxx_
Trible Kill: 双前导下划线: __xxx
Quadra Kil: 双前导下划线加末尾下划线: __xxx__
Penta Kill: 单下划线: _
1.单前导下划线 _xxx:
变量前面使用单下划线,用于指定该名称为“私有”。为方便自己或者阅读代码的人知道以“_”开头的名称只供内部使用,也就是说,如果你写如下代码:
from xxx import *,此时以“_”开头的名称都不会被导入,除非模块或包中的 “__all__”列表显示的包含了他
for example:
1 class Test: 2 def __init__(self): 3 self.foo = 11 4 self._bar = 23 5 # 如果此时实例化此类, 6 # 并尝试访问在__init__构造函数中定义 7 # 的foo和_bar属性 8 9 10 t = Test() 11 print(t.foo) 》》》11 12 print(t._bar) 》》》23
此时我们依然可以访问_bar,因为在python中_xxx仅仅是一个约定 至少对与变量和方法名是这样 BUT 前导的确实会影响从模块中导入 倒是PEP8又不推荐使用通配符导入 所以在遵循常规导入的情况下 单个下划线只是pytohn的一个命名约定成俗的规定 表示这个名称是供内部使用的
2.单末尾下划线 xxx_:
按照PEP8规定 单末尾下划线也是一个约定 用来避免与python关键字产生命名冲突 例如 你想用class来用作变量名称 可class又是pytohn 关键字 此时 class_就解决了这个问题
3.双前导下划线__xxx:
名称(具体为一个方法名)前双下划线(__)的用法并不是一种惯例,对解释器来说它有特定的意义。Python中的这种用法是为了避免与子类定义的名称冲突。Python文档指出,“__spam”这种形式(至少两个前导下划线,最多一个后续下划线)的任何标识符将会被“_classname__spam”这种形式原文取代,在这里“classname”是去掉前导下划线的当前类名。
例如下面的例子:
>>> class A(object):
... def _internal_use(self):
... pass
... def __method_name(self):
... pass
...
>>> dir(A())
['_A__method_name', ..., '_internal_use']
正如所预料的,“_internal_use”并未改变,而“__method_name”却被变成了“_ClassName__method_name”。此时,如果你创建A的一个子类B,那么你将不能轻易地覆写A中的方法“__method_name”。
>>> class B(A):
... pass
...
>>> dir(B())
['_A__method_name', '_B__method_name', ..., '_internal_use']
这里的功能几乎和Java中的final方法和C++类中标准方法(非虚方法)一样。
4、双前导和双末尾下划线 _var_
也许令人惊讶的是,如果一个名字同时以双下划线开始和结束,则不会应用名称修饰。 由双下划线前缀和后缀包围的变量不会被Python解释器修改:
classPrefixPostfixTest:
self.__bam__=42
>>> PrefixPostfixTest().__bam__
42
但是,Python保留了有双前导和双末尾下划线的名称,用于特殊用途。 这样的例子有,__init__对象构造函数,或__call__ --- 它使得一个对象可以被调用。
这些dunder方法通常被称为神奇方法 - 但Python社区中的许多人都不喜欢这种方法。
最好避免在自己的程序中使用以双下划线(“dunders”)开头和结尾的名称,以避免与将来Python语言的变化产生冲突。
5、单下划线 _
通常情况下,会在以下3种场景中使用:
1、在解释器中:在这种情况下,“_”代表交互式解释器会话中上一条执行的语句的结果。这种用法首先被标准CPython解释器采用,然后其他类型的解释器也先后采用。
>>> _ Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_' is not defined
>>> 42
>>> _
42
>>> 'alright!' if _ else ':('
'alright!'
'alright!'
2、作为一个名称:这与上面一点稍微有些联系,此时“_”作为临时性的名称使用。这样,当其他人阅读你的代码时将会知道,你分配了一个特定的名称,但是并不会在后面再次用到该名称。例如,下面的例子中,你可能对循环计数中的实际值并不感兴趣,此时就可以使用“_”。
n = 42
for _ in range(n):
do_something()
3、国际化:也许你也曾看到”_“会被作为一个函数来使用。这种情况下,它通常用于实现国际化和本地化字符串之间翻译查找的函数名称,这似乎源自并遵循相应的C约定。
例如,在Django文档“转换”章节中,你将能看到如下代码:
from django.utils.translation import ugettext as _
from django链接 import HttpResponse
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
可以发现,场景二和场景三中的使用方法可能会相互冲突,所以我们需要避免在使用“_”作为国际化查找转换功能的代码块中同时使用“_”作为临时名称。
总结:
Python下划线命名模式 - 小结
以下是一个简短的小结,即“速查表”,罗列了本文中谈到的五种Python下划线模式的含义:
模式 | 举例 | 含义 |
单前导下划线 | _xxx | 命名约定,仅供内部使用,通常不会由python解释器强制执行 只作为提示 |
单末尾下划线 | xxx_ | 按约定避免和python 内置关键字冲突 |
双前导下划线 | __xxx | 当在类上下文你中使用时 触发 名称修饰 python 强制执行 |
双前导加双末尾下划线 | __xxx__ | 表示python 语言定义的特殊方法 尽量避免在自己的属性中使用 (伪私有属性) |
单下划线 | _ |
不会总结了。。。。。。。。。看上边吧 |