双下划线触发名称改写而非私有化,仅用于避免子类命名冲突;实际存储为_classname__attr,外部或子类需用改写名访问,不适用于api字段或property底层变量。

双下划线触发名称改写,不是真正的私有
Python 没有真正意义上的私有属性,__name 这种双下划线前缀会触发名称改写(name mangling),变成 _ClassName__name。这主要是为避免子类意外覆盖父类同名属性,不是为了封装或访问控制。
常见错误是以为加了双下划线就“安全”了,结果在子类里用 self.__name 访问不到——因为子类的 __name 被改写成了 _SubClass__name,而父类的是 _Parent__name,根本不是同一个变量。
- 只在类内部使用
self.__attr;外部或子类中直接访问会报AttributeError - 若需从子类访问父类的双下划线属性,得用改写后的名字:
self._Parent__attr - 单下划线
_attr是约定俗成的“内部使用”,不触发改写,更灵活也更常用
什么时候该用双下划线?只用于避免子类命名冲突
典型场景是类库作者定义内部字段,又怕用户继承时无意重名。比如一个基类想存一个 __id,而子类也可能定义自己的 __id ——双下划线确保两者互不干扰。
反例:给 API 参数、配置项、数据模型字段加双下划线,纯属自找麻烦。用户调用时得记住改写规则,IDE 补全失效,调试器显示怪异名字,还容易误判为“不可见”。
- 适合:
class CacheManager:里用self.__cache防止子类覆盖 - 不适合:
class User:里用self.__email作为业务字段 - 如果只是想“建议别碰”,用单下划线
self._email更合理
如何检查是否被正确改写?用 dir() 或 vars()
运行时不确定某个属性是否被改写,最直接的方法是打印实例的属性列表:
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
class A:
def __init__(self):
self.__x = 1
<p>a = A()
print(dir(a)) # 会看到 '_A<strong>x',但看不到 '</strong>x'</p>
注意:__dict__ 里键名也是改写后的形式,所以 a.__dict__ 显示 {'_A__x': 1},而非 {'__x': 1}。
-
dir(obj)返回所有属性名(含改写后名字),适合快速排查 -
vars(obj)只返回实例字典,键名已改写,适合确认实际存储结构 - 不要依赖
hasattr(obj, '__x')——它会返回False,即使值存在
和 property 搭配时,双下划线要小心用在底层字段
用 @property 封装字段时,底层存储变量常习惯加下划线。但如果用 __value,会导致 getter/setter 内部也要用改写名,可读性差且易错。
class Counter:
def __init__(self, init=0):
self.__count = init # 实际存储用双下划线
<pre class="brush:python;toolbar:false;">@property
def count(self):
return self._Counter__count # 必须写全改写名,难维护
更稳妥的做法是底层用单下划线,接口用 property 控制访问:
class Counter:
def __init__(self, init=0):
self._count = init # 简洁、清晰、无改写
<pre class="brush:python;toolbar:false;">@property
def count(self):
return self._count
- 双下划线 + property 组合会让代码变脆,尤其涉及继承或动态属性时
- property 本身已是访问控制机制,底层字段用
_count足够表达意图 - 真需要拦截所有访问(包括反射),得靠
__getattribute__,不是靠双下划线
名称改写只解决子类命名冲突这一件事,它不提供保护、不阻止访问、也不替代设计契约。多数时候,_name 加文档说明,比硬套 __name 更可靠。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










