
下划线在 Python 里用途远不止“分隔单词”。一共有六种不同写法,每种都有明确的含义和约定,弄混了要么代码出 bug,要么让同事看不懂你在写什么。
往期阅读>>>
Python 20 个文本分析的库:效率提升 10 倍的秘密武器
Python 自动化管理Jenkins的15个实用脚本,提升效率
App2Docker:如何无需编写Dockerfile也可以创建容器镜像
Python 自动化识别Nginx配置并导出为excel文件,提升Nginx管理效率
_)在交互式解释器里,_ 保存着上一次表达式的结果,临时运算很方便:
>>>10+2030>>>_30# _ 保存了上一次的结果>>>_*260
写数字字面量时,_ 可以当千位分隔符用,纯粹是为了可读性,运行时会被忽略:
large_number = 1_000_000print(large_number) # 输出: 1000000price = 9_999.99print(price) # 输出: 9999.99
循环里用 _ 表示“这个值我不用”:
# 只跑五次,不需要索引for_inrange(5):print("Hello, Python!")# 解包时丢掉不需要的位置data = (1, 2, 3, 4, 5)first, _, third, _, fifth = dataprint(first, third, fifth) # 输出: 1 3 5
__xx__):魔法方法这是 Python 内置的“特殊方法”专用命名空间,__init__、__str__、__len__ 这些都属于这一类:
classMyClass:def__init__(self, value):self.value = valuedef__str__(self):returnf"MyClass with value: {self.value}"def__len__(self):returnlen(str(self.value))obj = MyClass(42)print(obj) # 调用 __str__print(len(obj)) # 调用 __len__
不要自己定义 __xxx__ 格式的名字——这个命名空间是 Python 保留的,随版本迭代可能会和新增的内置方法撞名。
_xx):内部使用以 _ 开头表示“这是内部实现,外部别直接用”。更实际的效果是:from module import * 不会导入这类名字。
mymodule.py:
defpublic_func():return"This is public"def_internal_func():return"This is internal"
导入行为的差异:
# 方式1:导入整个模块——两个都能用importmymoduleprint(mymodule.public_func()) # 正常print(mymodule._internal_func()) # 技术上可以,但不推荐# 方式2:通配符导入——只导入公共的frommymoduleimport*print(public_func()) # 正常# print(_internal_func()) # NameError:压根没被导入进来
xx_):绕过关键词冲突变量名恰好和关键词撞了,后面加 _ 就能用:
# class = "MyClass" # SyntaxErrorclass_ = "MyClass"def_ = "function"return_ = "value"print(class_, def_, return_) # 输出: MyClass function value
不过实际上能用的话,换一个完全不同的名字更好——class_ 读起来总归有点别扭。
__xx):名称修饰(Name Mangling)以 __ 开头的属性,Python 会在背后把它改名为 _ClassName__xx,目的是防止子类无意间覆盖父类的属性:
classParent:def__init__(self):self.public = "公共属性"self.__private = "私有属性(经过名称修饰)"defget_private(self):returnself.__privateclassChild(Parent):def__init__(self):super().__init__()self.__private = "子类的私有属性"# 不会覆盖父类的 __privatedefshow_all(self):print(f"公共属性: {self.public}")print(f"尝试访问父类私有属性: {self.get_private()}")print(f"子类自己的私有属性: {self.__private}")parent = Parent()print(parent.public) # 输出: 公共属性# print(parent.__private) # AttributeErrorchild = Child()child.show_all()# 公共属性: 公共属性# 尝试访问父类私有属性: 私有属性(经过名称修饰)# 子类自己的私有属性: 子类的私有属性
Parent 里的 __private 实际存储名是 _Parent__private,Child 里的是 _Child__private,两者互不干扰。
_ClassName__xx:直接访问被修饰后的名字既然知道改名规则,就可以绕过去直接访问:
classMyClass:def__init__(self):self.__secret = "隐藏的秘密"obj = MyClass()# print(obj.__secret) # AttributeErrorprint(obj._MyClass__secret) # 输出: 隐藏的秘密
这只适合调试或测试场景。正常业务代码里这样写,基本意味着设计出了问题。
六种写法放在一个类里看得更清楚:
classExample:def__init__(self):self.normal = "普通属性"self._internal = "内部属性"self.__private = "私有属性"defpublic_method(self):return"公共方法"def_internal_method(self):return"内部方法"def__private_method(self):return"私有方法"defshow_private(self):returnself.__private_method()ex = Example()print(ex.normal) # 正常print(ex._internal) # 可以访问,但约定上别这么做print(ex.show_private()) # 通过公共方法访问私有方法# print(ex.__private) # AttributeError# print(ex.__private_method) # AttributeError
六种规则里,__xx__ 不要碰(Python 的地盘),_xx 和 __xx 是日常最有用的两种——前者表示“我打算内部用”,后者表示“子类别动”,理解这两个区别就够用了。
