Python中的下划线
018.11.25
前言
我发现公司里即便是工作经验一年的程序员依然对Python的私有属性有些迷糊——也就是下划线的问题——拿捏不准要怎么用。我想在此总结一下。
单下划线
前置
前置单下划线是一种约定,即:认为这个变量或方法是类私有,外界不要调用。在这里,对象认为是“外界”。
示例如下:
class Persion(object):
def __init__(self, name, age, hobbies):
# 不建议被外界使用
self._name = name
self._age = age
self._hobbies = hobbies
# 允许被外界调用
@property
def detail(self):
return self._get_sentence()
# 建议不要被外界调用,而是在类中使用
def _get_sentence(self):
return f"{self._name} is {self._age} and loves {self._hobbies}."
if __name__ == "__main__":
lily = Persion("lily", 18, "listenning")
print(lily.detail)
# 输出:
# lily is 18 and loves listenning.
但外界能不能调用带前置单下划线的成员变量和成员方法呢?事实上是可以的:
print(lily._name) # 输出:lily
print(lily._get_sentence()) # 输出: lily is 18 and loves listening.
我实在记不清是《Python编程之美》还是《数据结构(Python描述)》,里面对此解释说:之所以Python对类私有只是一种约定,是认为使用Python开发的程序员应该是成熟的。
而另一方面,在一个模块中,前置单下划线的变量或是函数,不能通过from module import *
方式导入。如:
# demo1.py
# 这是一个变量
_one = "ONE"
# 这是一个函数
def _two():
print("TWO")
# demo2.py
from demo1 import *
print(_one) # 错误使用
_two() # 错误使用
对此解释器会抛出异常:NameError: name 'xxx' is not defined
。
但并非demo1中的前置单下划线变量/方法在demo2中就不可以使用,完全可以import module
,然后通过module.xxx
方式,demo2代码做如下调整:
# demo2.py
import demo1
print(demo1._one) # 正确使用
demo1._two() # 正确使用
后置
后置单下划线是一种编程风格建议,即:当你的变量/方法名与Python中的关键字冲突时,可以加上后置单下划线做区分。如:
list_ = ["lily", "bob", "green"]
dict_ = {
"name": "lily",
"age": 18
}
双下划线
前置
事实上,前置双下划线变量/方法,更像是类私有。将第一个示例代码改写如下(单下划线都改写为双下划线):
class Persion(object):
def __init__(self, name, age, hobbies):
# 不能被外界访问
self.__name = name
self.__age = age
self.__hobbies = hobbies
# 允许被外界调用
@property
def detail(self):
return self.__get_sentence()
# 不能被外界调用
def __get_sentence(self):
return f"{self.__name} is {self.__age} and loves {self.__hobbies}."
if __name__ == "__main__":
lily = Persion("lily", 18, "listening")
print(lily.detail)
# 输出:
# lily is 18 and loves listening.
可如果你想外界访问:
print(lily.__name) # 错误示例
print(lily.__get_sentence()) # 错误示例
解释器会抛异常:AttributeError: 'Persion' object has no attribute '__name'
。
其实同前置单下划线一样,双下划线其实也可以被外界访问,只不过解释器对他们**“以类之姓,冠它之名”**。
print(lily._Persion__name) # 输出:lily
lily._Persion__get_sentence() # 输出:lily is 18 and loves listening.
也就是说,当类名为X
,双下划线变量为__y
时,外界对其访问应该形如:_X__y
。
完全与前置单下划线一样的是,模块中的前置双下划线变量/函数,不能通过from module import *
导入,但可以import module
后使用:
module.__one
module.__two()
前置双下划线存在的意义:为了避免该成员的名称与子类中的名称冲突。
后置
开个玩笑。
后置双下划线既没有实际意义,也不是编程风格的建议。
前后置
稍微对Python熟悉的人就会知道,存在前置双下划线同时还有后置双下划线的变量以及方法。如常见的__name__
以及__init__()
。这些属于Python的魔法对象,是为了区别其他用户自定义的命名。官方表示:永远不要将这样的命名方式应用于自己的变量或函数。
感谢
- 参考GitHuber taizilongxu https://github.com/taizilongxu/interview_python
- 参考知乎er 权循真 https://www.zhihu.com/question/19754941