
Python 没有真正的访问控制机制
Python 不提供编译期或运行期的强制访问限制,__private 这类命名只是约定,不是锁。解释器不会阻止你调用 _protected 或读取 __mangled 属性——它只是悄悄改了名字,且改名规则可预测、可绕过。
双下划线 __name 触发名称改写(name mangling)
这是 Python 唯一在语法层面对“私有”做的处理,但目的不是封死访问,而是避免子类意外覆盖父类的内部属性。
-
__x在类A中会被改写为_A__x,而非__x - 改写只发生在类定义体内,
self.__x = 1会触发,但obj.__x = 2不会 - 子类
B(A)定义自己的__y,会变成_B__y,和_A__x互不干扰 - 直接访问
obj._A__x完全合法,且常见于调试、测试或 monkey patch 场景
单下划线 _name 是纯文档约定
它对解释器零影响,既不改名也不报错,唯一作用是告诉其他开发者:“这个别依赖,可能变”。ide 和 linter(如 pylint)会据此提示,但运行时毫无约束。
-
from module import *会忽略所有_name,这是它唯一被语言识别的地方 - 很多标准库用
_helper表示内部函数,但你 import 后照调不误 - 过度使用
_可能掩盖真实设计问题:如果真不该被外部用,是不是该拆模块、改接口?
想真正限制访问?得靠运行时检查或封装边界
语言不拦,你就得自己设卡。常见做法是结合 __getattribute__ 或 Property 做动态拦截,但代价明显:
- 重写
__getattribute__影响所有属性访问,容易误伤,性能下降显著 - 用
@property包装敏感字段,配合raise AttributeError,但仅对显式访问有效,无法防obj.__dict__直读 - 更实际的方案是:把不该暴露的逻辑放进独立模块,用
__all__控制导入,靠目录结构和命名规范划界 - 测试和 CI 阶段用 ast 检查或静态分析工具(如 pyright 的 private usage warning)提前发现越界调用
真正难的不是“怎么写 private”,而是判断哪些状态/行为必须隔离、哪些其实可以开放——Python 的哲学是信任使用者,而不是用语法捆住手脚。