平时写代码经常碰到下划线、双下划线等“奇怪”的写法,今天特意花时间彻底解决这个问题。文章内容主要参考《深入理解Python特性》一书。
前置单下划线:_var
意义:python社区约定好单下划线表达的是某种意思,本身不会影响程序的行为
- PEP8中定义:以单个下划线开头的变量或方法只在内部使用
- 这个符号对python解释器没有什么用,它只是个小小的提示:这个变量或方法并不是这个类的公共接口,最好不要使用它——(因为python中没有严格界定“私有”和“公有”变量,因此下划线可以作为提示)
- 使用通配符导入模块,Python不会导入带有前置但下划线的名称,除非模块中定义了__all__列表覆盖了这个行为,举个例子:
# test_import.py文件内容
def hello():
print('Hello, world!')
def _world():
print('Hello, world!')
>>> from test_import import *
>>> hello()
Hello, world!
>>> _world()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_world' is not defined
后置单下划线:var_
意义:名称被Python语言中的关键字占用的时候,就可以使用,比如class
,def
这些,使用class_
,def_
代替
前置双下划线:__var
这个估计经常遇到
意义:告诉python解释器重写属性名称,避免子类中的命名冲突。
- 也称为:名称改写(name mangling)
- 用
dir()
可以查看对象的属性和方法 - 注意类似
__bar
前面双下划线的方法和变量,
class Test:
def __init__(self):
self.foo = 12
self._bar = 24
self.__bar = 43
class ExtendedTest(Test):
def __init__(self):
super(ExtendedTest, self).__init__()
self.foo = 'hello'
self._bar = 'world'
self.__bar = 'down'
当创建ExtendedTest
类的实例t时,调用t.__bar
会报错AttributeError: 'ExtendedTest' object has no attribute '__bar'
。
具体来看一下原因:输入dir(t)
>>> dir(t)
['_ExtendedTest__bar', '_Test__bar', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__
', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_bar', 'foo']
我们发现t
没有__bar
属性,而是变成了_ExtendedTest__bar
和_Test__bar
。这是因为Python进行了名称重写。
继续输入:t._ExtendedTest__bar
和t__Test__bar
,出现了down
和43
>>> t._ExtendedTest__bar
'down'
>>> t._Test__bar
43
- 我们无法察觉双下划线名称的改写,也就是我们无法通过t
.__bar
直接访问__bar
的值,只能借的类方法去访问或者是通过t._ExtendedTest__bar
。类似举例如下:
class ManglingTest:
def __init__(self):
self.__mangled = 'hello'
def get_mangled(self):
return self.__mangled
>>> ManglingTest().get_mangled()
'hello'
>>> ManglingTest().__mangled
AttributeError: "'ManglingTest' object has no attribute '__mangled'"
- 名称改写也适用于方法名。
class MangledMethod:
def __method(self):
return 42
def call_it(self):
return self.__method()
>>> MangledMethod().__method()
AttributeError: "'MangledMethod' object has no attribute '__method'"
>>> MangledMethod().call_it()
42
- 还有更神奇的用法:由于名称的改写,因此类中
test()
方法用__mangled
就可以访问在MangledGlobal
类中访问全局变量_MangledGlobal__mangled
,因为Python的名称改写自动扩展了变量名。
_MangledGlobal__mangled = 23
class MangledGlobal:
def test(self):
return __mangled
>>> MangledGlobal().test()
23
前后双下划线:__ var__
意义:如果使用前后双下划线,那么则不会发生名字重写,不受python解释器的影响。
- 双下划线方法称为魔法方法,最好避免在自己程序中使用双下划线开头和结尾的名称,避免于python语言未来版本变更发生冲突。
- python里面有很多内置方法,如:
__hash__
,__iter__
,__dict__
和__repr__
等其他内置函数,都是用前后双下划线
单下划线:_
- 表明变量是临时的或者无关紧要
for _ in range(10):
print('Hello, world! ')
- 表示解释器计算的上一个表达式的结果
>>> 20 + 3
23
>>> _
23
>>> print(_)
23
>>> list()
[]
>>> _.append(1)
>>> _.append(2)
>>> _.append(3)
>>> _
[1, 2, 3]
总结:
- 前置单下划线_var:命名约定,表明仅在内部使用(在通配符import * 的时候会有小小的区别),一般对python解释器没有特殊的含义
- 后置单下划线:var_:命名约定,避免与python关键字命名冲突
- 前置双下划线:__var:名称重写,在类环境中使用会触发名称改写,对python解释器有影响
- 前后双下划线:__var__:python定义的魔法方法,在自定义属性中逼民啊用这种命名方式
- 单下划线:_:表明变量是临时的或者无关紧要。此外还可以表示Python REPL会话中的上一个结果。
结束了…