python私有变量__name并非真正私有,而是通过名称改写(如_a__name)避免子类命名冲突;单下划线_name仅为约定,无机制约束;动态添加的__x不触发改写。

Python私有变量不是真私有,只是约定+名称改写
Python没有强制访问控制,__name 这种双下划线开头的变量不会被禁止访问,而是触发名称改写(name mangling):解释器自动把它变成 _ClassName__name。这是为了防止子类意外覆盖父类的内部属性,不是为了“锁住”数据。
常见错误现象:AttributeError: 'Child' object has no attribute '__value' —— 你以为子类没继承,其实是它被改名成了 _Parent__value,而你还在用原名访问。
- 只对类定义中以
__开头且不以__结尾的标识符生效(比如__init__不改写) - 改写发生在编译期,不是运行时,所以动态拼接字符串访问(如
getattr(obj, '_A__x'))依然有效 - 如果类名是
A,__x→_A__x;如果是MyClass,__data→_MyClass__data
单下划线 _name 是约定,不是机制
_name 表示“请当它是私有的”,仅靠文档和开发者自觉。IDE 会标灰、from module import * 默认忽略它,但解释器完全不管。
使用场景:内部方法、临时缓存、不想暴露给使用者但又不需要防子类冲突的字段。比如 _cache、_parse_config()。
- 别指望它阻止别人调用——有人直接用
obj._internal,你拦不住 - 和双下划线混用会混乱:
__x和_x同时存在时,它们是两个完全独立的变量 - 某些工具链(如 dataclasses、pydantic)对
_开头字段有特殊处理,注意查阅对应文档
怎么安全访问被改写的私有变量(不推荐但有时必须)
极少数情况需要绕过封装,比如调试、测试或与遗留代码对接。这时得手动构造改写后的名字,不能硬写 __x。
错误做法:obj.__x → 报错;正确做法:obj._ClassName__x,但类名不能写死。
- 用
dir(obj)查看实际属性名,找_A__x这类模式 - 动态获取:
getattr(obj, f'_{type(obj).__name__}__x'),但要注意多继承时可能有歧义 - 更稳妥的是在类里暴露一个受控的 getter 方法,而不是依赖名称改写细节
双下划线改写在继承和多重继承里容易翻车
子类也定义同名 __field,会导致两个独立的改写变量:_Parent__field 和 _Child__field。表面看是“覆盖”,实际是并存。
性能影响几乎为零,但可读性和维护性暴跌——没人能一眼看出哪个 __x 对应哪个类。
- 多重继承时,如果 A 和 B 都有
__state,C(A, B) 实例会有_A__state和_B__state两个字段 - 用
super().__init__()不会自动代理双下划线属性,得显式调用super()._Parent__setup()(不推荐) - 真正需要隔离的内部状态,优先考虑用闭包、弱引用字典或单独的 helper 类,而不是依赖名称改写
名称改写不是访问控制,而是避免命名冲突的辅助手段。最常被忽略的一点:它只作用于类体内的定义,对实例动态设置的 obj.__x = 1 完全无效——那个 __x 就是真的叫 __x,不会被改写。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











