Python 对象名前的单下划线和双下划线有什么区别?
单下划线 _name 是"内部使用"的命名约定(不强制),双下划线 __name 触发名称改写(name mangling)避免子类属性冲突。本文详解两者区别与适用场景。
一句话区别:_name(单下划线)只是约定俗成的"私有"标记,靠自觉,外部照样能访问;__name(双下划线)会触发解释器的名称改写机制,属性名被改成 _类名__name,主要用来避免父子类之间的意外同名冲突,两者都不是真正的私有。
单下划线:约定大于强制
class Service:
def __init__(self):
self._cache = {} # 提示:内部实现,请勿直接动
s = Service()
print(s._cache) # 可以访问,只是不该这么做
单下划线向读代码的人(和 IDE)传达"这是内部细节"的信号。from module import * 时,单下划线开头的名字默认不会被导入。
双下划线:名称改写
class MyClass:
def __init__(self):
self.__private = 42
obj = MyClass()
obj.__private # AttributeError
obj._MyClass__private # 42,改写后的真实名字
以两个下划线开头(且不以两个下划线结尾)的名字会被解释器改写成 _类名__原名。这样父类的 __state 和子类的 __state 实际是两个不同属性,不会互相覆盖。
其他下划线形式速查
| 写法 | 含义 |
|---|---|
_name |
约定私有 / 临时占位 |
__name |
名称改写,防子类冲突 |
__name__ |
Python 内置魔法名(如 __init__),不要自己造 |
_ 单独使用 |
交互解释器中表示上一个结果;代码里表示"不关心这个变量"(如 for _ in range(3)) |
观测云对照
用命名约定划分 SDK 封装边界。 在内部封装的日志上报 SDK 里,把不希望业务方触碰的缓冲队列、发送线程命名为 _buffer、_sender,配合文档说明;观测云 APM 探针本身的内部字段也遵循同样约定,业务方只面向公开 API 编程。
常见问题(FAQ)
Q:双下划线能实现真正的私有吗?
A:不能。_MyClass__private 依然能访问到,它只是防"意外",不防"故意"。
Q:自己定义 __foo__ 这样的名字可以吗?
A:不建议。这种形式保留给语言本身,未来版本新增魔法方法可能与之冲突。
Q:父类想保护属性不被子类覆盖,用哪个?
A:用双下划线 __attr,名称改写后子类的同名属性不会覆盖父类的。