__name无法被外部直接访问是因为Python在编译期执行名称改写,将其重命名为_ClassName__name(如_Person__name),该操作发生在类定义阶段,仅对以__开头且不以__结尾的标识符生效,旨在避免子类命名冲突而非实现封装。

__name 无法被外部直接访问,不是因为 Python 拦截了访问请求,而是它根本就不存在于实例的属性字典里——解释器在定义时就悄悄把它改名了。
名称改写(Name Mangling)是真实发生的编译期操作
当你在类中写 self.__name = "Alice",Python 不会把属性存为 __name,而是立即重命名为 _ClassName__name(比如 _Person__name)。这个动作发生在类定义阶段,不是运行时才查表判断。
- 你可以用
obj.__dict__.keys()查看实际存储的属性名,__name不会出现,但_Person__name一定在 -
dir(obj)也会显示改写后的名字,而不会列出原始双下划线名 - 这个规则对所有以
__开头、且不以__结尾的标识符生效;像__init__或__str__这类魔法方法不受影响
子类中同名私有属性不会覆盖父类的私有属性
这是名称改写最实际的用途:避免意外覆盖。父类的 __id 变成 _Parent__id,子类的 __id 变成 _Child__id,两者完全独立。
- 子类方法里直接写
self.__id访问的是自己的私有属性,不是父类的 - 子类无法通过
super()或任何常规方式“继承”父类的私有属性 - 如果你在子类里调用父类公有方法(比如
super().show()),该方法内部仍能正常访问self._Parent__id
强行访问 _ClassName__name 是可行但危险的
虽然你能写 obj._Person__name 拿到值,但这不是“绕过封装”,而是直接操作解释器生成的内部标识符。
- 不同 Python 实现(如 CPython、PyPy)或未来版本可能调整名称改写规则
- IDE 和类型检查工具(如 mypy)会忽略这种访问,导致静态分析失效
- 一旦类名变更,所有硬编码的
_OldName__name都会失效
真正该用 __ 的地方,其实是防止子类命名冲突
很多人误以为 __ 是为了“隐藏数据”,其实它主要解决的是类继承中的名字污染问题。如果你不需要子类,或者只是想表达“别乱动”,用单下划线 _name 更合适、更清晰。
立即学习“Python免费学习笔记(深入)”;
-
__不适合做权限控制,也不该用于 API 设计中的“私有接口” - 需要受控读写时,优先用
@property+_name,而不是__name - 动态添加的属性(如
obj.__name = "Bob")不会触发名称改写,它就是个普通属性,和类内定义的self.__name完全无关


















