Python 下划线


  • 单前导下划线:_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.zhangsanself._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 定义的特殊方法。
_ 临时、无关紧要的变量。
在交互式解释器中,表示最近一个表达式的结果。

posted @ 2021-12-20 17:20  做梦当财神  阅读(104)  评论(0编辑  收藏  举报