双下划线私有属性无运行时开销,名称改写在类定义时静态完成,字节码直接使用\_ClassName\_\_attr,访问为O(1);@property才有函数调用开销,单下划线\_age是零开销的约定式私有。

Python 中的双下划线私有属性没有运行时开销,所谓“减少名称改写开销”是个伪命题——它只发生在编译/定义阶段,不参与每次属性访问。
为什么 __attr 不影响运行性能
名称改写(name mangling)是 Python 解释器在类定义时一次性完成的静态操作,不是每次 obj.__attr 或 self.__attr 访问时动态计算的。生成的字节码直接使用改写后的名字(如 _ClassName__attr),和普通属性访问一样是 O(1) 查字典操作。
- 你写
self.__age,解释器在定义类时就把它替换成self._Person__age,后续所有调用都走这个键 -
getattr(obj, '__age')会失败,因为实际键名已变;但getattr(obj, '_Person__age')成功——这说明改写是确定且提前完成的 - 用
dis.dis看字节码,会发现对self.__age的 LOAD_ATTR 指令目标就是_Person__age,无额外分支或字符串拼接
@property 才真有开销,但它解决的是逻辑问题,不是“绕过改写”
很多人误以为用 @property 是为了“避免双下划线”,其实它是为封装访问逻辑(校验、转换、延迟计算等)。它的开销来自函数调用本身,而非名称机制。
- 纯数据代理型
@property(如只 returnself._age)比直接访问self._age慢约 2–3 倍,但比绝大多数业务逻辑轻得多 - 若你不需要校验、日志、缓存等,又想保持“约定式私有”,直接用单下划线
_age即可:不触发 name mangling,语义清晰,零额外开销 - 双下划线唯一真实成本是调试时多打几个字符(
_Cls__x),以及 IDE 补全可能不显示——这不是性能问题,是可读性权衡
真正该警惕的“优化陷阱”
试图用反射或 __dict__ 手动拼接名称来“加速”私有访问,反而引入 bug 和维护风险:
立即学习“Python免费学习笔记(深入)”;
-
obj.__dict__.get('_' + obj.__class__.__name__ + '__age')—— 错!子类继承时类名不对,且忽略描述符、__slots__等边界情况 - 把
__age改成_age后又加@property包一层,纯属叠床架屋 - 为省一次函数调用而放弃
@property的校验能力,在金融、配置等场景可能引发严重数据错误
名称改写不是瓶颈,是设计信号。需要极致性能时,优先考虑算法、I/O 或 C 扩展;纠结 __x 和 _x 的“开销”,通常意味着没分清封装意图和性能瓶颈的真实位置。


















