- 单前导下划线:
_var
- 单末尾下划线:
var_
- 双前导下划线:
__var
- 双前导和末尾下划线:
__var__
- 单下划线:
_
文章结尾有”速查表“。
一、单前导下划线_var
以单下划线开头的变量或方法理论上仅供内部使用,执行时不会强制执行。
class Test:
def __init__(self):
self.zhangsan = 11
self._lisi = 22
访问 foo
和 _bar
属性:
t = Test()
print(t.zhangsan)
print(t._lisi)
输出:
11
22
单前导下划线没有阻止访问属性的值。
单前导下划线会影响从模块中导入名称的方式。(除非模块定义了覆盖此行为的__all__
列表)
# age.py
def zhangsan():
return 11
def _lisi():
return 22
使用通配符从模块导入所有名称:
# test.py
from age import *
print(zhangsan())
print(_lisi())
输出:
11
NameError: name '_lisi' is not defined
应避免通配符导入,因为导入哪些名称会不清楚。坚持常规导入。
import age
print(age.zhangsan())
print(age._lisi())
输出:
11
22
二、单末尾下划线var_
解决与关键字的命名冲突。
def zhangsan(name, class):
pass
def zhangsan(name, class_):
pass
输出:
SyntaxError: invalid syntax
三、双前导下划线__var
双前导下划线会使 Python 重写属性名称,为防止变量的属性在子类中被重写。
这也叫名称修饰。
class Test:
def __init__(self):
self.zhangsan = 11
self._lisi = 22
self.__wangwu = 33
t = Test()
print(dir(t)) # 查看对象属性
输出:
['_Test__wangwu', '__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__', '_lisi', 'zhangsan']
从属性列表看出:
self.zhangsan
,self._lisi
变量的属性为zhangsan
,_lisi
。
self.__wangwu
属性为_Test__wangwu
。
继承Test
类:
class Test:
def __init__(self):
self.zhangsan = 11
self._lisi = 22
self.__wangwu = 33
class ExtendedTest(Test):
def __init__(self):
super().__init__()
self.zhangsan = 111
self._lisi = 222
self.__wangwu = 333
t = Test()
t2 = ExtendedTest()
print(t2.zhangsan)
print(t2._lisi)
print(t2.__wangwu)
输出:
111
222
AttributeError: 'ExtendedTest' object has no attribute '__wangwu'
名称修饰被再次触发。这个对象没有__wangwu
属性。而是_ExtendedTest__wangwu
属性。
print(dir(t2))
输出:
['_ExtendedTest__wangwu', '_Test__wangwu', '__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__',
'_lisi', 'zhangsan']
_Test__wangwu
属性还在:
print(t._Test__wangwu)
# 33
双前导下划线名称修饰对程序员完全透明。
class Name:
def __init__(self):
self.__name = 'zhangsan'
def get_name(self):
return self.__name
print(Name().get_name())
print(Name().__name)
输出:
zhangsan
AttributeError: 'Name' object has no attribute '__name'
名称修饰也适用于方法名称。
其实,名称修饰会影响一个类中所有以两个下划线开头的名称。
class People:
def __age(self):
return 44
def call_it(self):
return self.__age()
print(People().__age())
print(People().call_it())
输出:
AttributeError: 'People' object has no attribute '__age'
44
又一个令人惊讶的名称修饰的例子:
_PeopleGlobal__age = 55
class PeopleGlobal:
def test(self):
return __age
print(PeopleGlobal().test())
# 55
声明一个全局变量_PeopleGlobal__age
。然后在PeopleGlobal
类中访问变量。
由于名称修饰,能在test()
方法内,以__age
引用全局变量。Python 自动将__age
扩展为_PeopleGlobal__age
,因为他以两个下划线开头。
这表明名称修饰不是专门与类属性关联的。它适用于类中所有以两个下划线开头的名称。
四、双前导和双末尾下划线__var__
同时以双下划线开始和结束,不会应用于名称修饰。即不会被 Python 修改。
但用于特殊用途。比如:__init__
是构造函数。__call__
可以使一个对象被调用。
最好避免使用以双下划线开头和结尾的名称,防止冲突。
五、单下划线_
表示变量是临时或无关紧要的。
for _ in range(6):
print('Hello, world!')
也可用于拆分表达式中:
# 只对颜色、里程感兴趣
car = ('red', 'auto', 12, 3812.6)
color, _, _, mile = car
print(color, mile)
_
是交互式解释器的一个特殊变量,表示最近一个表达式的结果。
>>> 20+3
23
>>> 20-3
17
>>> _
17
>>> list()
[]
>>> _.append(1)
>>> _.append(2)
>>> _.append(3)
>>> _
[1, 2, 3]
六、总结
模式 | 含义 |
---|---|
_var |
命名约定,理论上仅供内部使用。但不会强制执行,只作为对程序员的提示。 |
var_ |
避免与 Python 关键字命名冲突。 |
__var |
在类中触发 “名称修饰”。由 Python 强制执行。 |
__var__ |
Python 定义的特殊方法。 |
_ |
临时、无关紧要的变量。 在交互式解释器中,表示最近一个表达式的结果。 |