Python双下划线属性是名称改写而非访问控制,解释器自动将__attr重命名为_ClassName__attr以避免子类冲突,但不阻止外部访问。

Python 的双下划线属性不是访问控制机制,而是名称改写(name mangling)——它防的是误触和子类覆盖,不是外部访问。
__attr 为什么能被 _ClassName__attr 访问
Python 解释器在类定义阶段,会把所有满足条件的 __attr 自动重命名为 _ClassName__attr。这个过程完全透明、可预测,不加密也不拦截。只要你知道类名和原属性名,就能拼出真实键名。
常见错误现象:
- 以为
obj.__attr报错 = 属性不存在 → 实际是键名已变,obj.__dict__里根本找不到__attr,只有_ClassName__attr - 手动给实例赋值
obj.__attr = 123→ 这创建的是一个全新的、和类内私有属性完全无关的实例属性
实操建议:
- 用
dir(obj)或obj.__dict__查看真实存在的键名,确认是否已被 mangling - 不要在外部代码中硬写
_ClassName__attr—— 类名一改就失效,且破坏封装意图 - 如果真需要读写,优先走公开方法(如
get_x()/set_x()),或用@property
双下划线真正的设计目的:隔离子类命名冲突
它的唯一强理由,是让父类和子类可以各自定义同名“私有”属性,互不干扰。这不是为了拦住同事,而是防止继承时意外覆盖。
使用场景:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 你在写一个基类,内部需要一个状态变量(比如
__cache),又不想让任何子类无意中通过定义同名self.__cache = ...覆盖它 - 你在实现 descriptor 或元类逻辑,需要藏一个绝对只属于当前类的标记字段
性能 / 兼容性影响:
- 名称改写发生在类创建时,无运行时开销
- 所有 CPython、PyPy、Jython 都遵循同一规则,跨解释器兼容
为什么不能靠双下划线做数据保护
因为 Python 没有访问控制语法。所有对象结构在运行时都是可查、可改的。你拦不住 setattr(obj, '_ClassName__secret', 'hacked'),也拦不住 monkey patch。
容易踩的坑:
- 把
__当成安全边界,在敏感逻辑(如权限校验、密钥存储)里依赖它 → 实际毫无防护力 - 在 pickle / JSON 序列化时直接操作
__dict__,却忘了里面全是 mangling 后的名字,导致反序列化失败 - 用 IDE 的自动补全看到
_ClassName__attr就顺手用了 → 这属于打破契约,后续重构极易断裂
真正难的不是怎么访问,而是什么时候不该访问:当你发现自己总想绕过 __ 去读写,通常说明接口设计漏了必要方法,或者职责划分出了问题。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










