0. 背景
Python里面的下划线“_”使用频率明显比其他主流语言要多的多,而且在Python中,它也有自己独到的用处。本文主要简述:下划线在python中的使用常识。
1. 单下划线-将名称封装到类中
如果想将类中的“私有”数据封装到类的实例上,但是又需要考虑到Python缺乏对属性的访问控制问题。与其依赖语言特性来封装数据,Python程序员们更期望通过特定的命名规则来表达出对数据和方法的用途。
第一个规则是任何以单下划线(_)开头的名字应该总是被认为只属于内部实现。
比如:
class A:
def __init__(self):
self._internal = 0 # 前缀加入"_",是类中的一个“内部(internal)”属性
self.public = 1 # 类中一个“公开(public)”属性
def public_method(slef):
‘‘‘
一个public方法
‘‘‘
pass
def _internal_method(self):
‘‘‘
一个内部(internal)方法
‘‘‘
pass
Python本身并不会阻止其他人访问内部名称。但是如果有人这么做了,则被认为是粗鲁的,而且可能导致产生出脆弱不堪的代码。应该要提到的是,以下划线为前缀的标识也可用于模块名称和模块级的函数。比如,如果见到有模块名以下划线前缀(例如,_socket
),那么它就属于内部实现。同样的,模块级函数比如sys._getframe()
使用起来也要格外小心。
2. 双下划线-名称重整(name mangling
)
我们应该在类定义中也见到过以双下滑线(__)为前缀的名称。例如:
class B:
def __init__(self):
self.__private = 0 # 名称重整后为:_B__private
def __private_method(self):
‘‘‘
名称重整后为:_B__private_method
‘‘‘
pass
def public_method(self):
self.__private_method()
以双下划线为前缀的名称会导致出现名称重整(
name mangling
)的行为。这样的属性不能通过继承而覆盖。
具体来说就是上面这个类中的私有属性会被分别重命名为_B__private
和_B__private_method
。其实类似这样的名称重整的目的就是为了继承——这样的属性不能通过继承而覆盖。
class C(B):
def __init(self):
super().__init()
self.__private = 1 # 此语句不会覆盖B.__private
‘‘‘ 此方法不会覆盖 B.__private_method(),
因为名称重整后会成名称重整后为:_B__private_method,
而此函数重整后为: _C__private_method
‘‘‘
def __private_method(self):
pass
这里,私有名称__private
和__private_method
会被重命名为_C__ptvate
和_C_private_method
,这和基类B中的重整名称不同。
“私有”属性存在两种不同的命名规则(单下划线和双下划线),这一事实引出了一个显而易见的问题:应该使用那种风格?
对于大部分代码而言,我们应该让非公有名称以单下划线开头。但是,如果我们知道代码中会涉及子类化处理,而且有些内部属性应该对子类进行隐藏,那么此时就应该使用双下划线开头。
3. 后缀单下划线-避免关键字冲突
此外还应该指出的是,有时候可能想定义一个变量,但是名称可能会和保留字产生冲突。基于此,应该在名称最后加上一个单下划线以示区别。比如:
lambda_ = 2.0 # 添加后缀下划线_ ,以避免与关键字"lambda"冲突
这里不采用以下划线前缀的原因是避免在使用意图上发生混淆,如果采用下划线开头的形式,那么可能会被解释为私有变量,而不是被认为是避免关键冲突。