python双下划线私有属性通过名称修饰变为\_classname\_\_attr,旨在防止继承时意外覆盖,而非真正隐藏;它不作用于动态赋值、特殊方法(如\_\_init\_\_),且子类同名属性会被独立修饰。

Python 中双下划线私有属性不是真私有,而是通过名称修饰(name mangling)把 __attr 自动重命名为 _ClassName__attr,目的是防误覆盖,不是防访问。
名称修饰的具体规则
当类中定义形如 self.__id 或 def __helper(self) 的成员时,Python 会在实例化后自动将其名称改为 _类名__原名 格式:
- 仅对类定义内部、以双下划线开头且不以双下划线结尾的标识符生效(
__init__不触发,__value触发) - 修饰发生在类体执行阶段,与实例创建时机无关
- 子类若定义同名
__x,会被修饰为_子类名__x,从而和父类的_父类名__x隔离,避免意外覆盖 - 修饰只作用于类内定义的属性/方法,对动态赋值(如
obj.__y = 123)不生效
为什么 obj.__attr 会报 AttributeError?
因为解释器根本没在对象的 __dict__ 里存 __attr 这个键,它只存了修饰后的名字。典型现象:
-
print(obj.__attr)→ 报错:'MyClass' object has no attribute '__attr' -
print(obj.__dict__)→ 输出类似{'_MyClass__attr': 42} -
print(obj._MyClass__attr)→ 正常输出42(但这是绕过约定,不推荐)
常见误用与兼容性陷阱
名称修饰看似“加私有”,实则带来几个隐蔽坑点:
- 继承链中调试困难:子类无法直接访问父类的
__x,必须用_ParentClass__x,而父类名一旦改名,所有这类硬编码访问全崩 - 序列化/反序列化失败:
pickle或json.dumps(obj)默认只处理__dict__中的键,但外部代码若按原名__x去取值,必然 KeyError - 动态属性操作失效:
getattr(obj, '__x', None)返回None,必须写成getattr(obj, '_MyClass__x', None) - IDE 和类型检查器(如 mypy)通常能识别修饰逻辑,但自定义元类或
__getattribute__覆盖不当,容易破坏修饰行为
真正需要封装时,优先用单下划线 _attr + 文档说明 + property 控制读写;只有明确要隔离父子类同名内部成员时,才用双下划线——它本质是“继承保护机制”,不是“数据保密机制”。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











