子类无法直接访问父类私有字段,应通过@property和@setter封装实现安全继承;子类可重写属性方法并调用父类逻辑,避免直接访问重整后的名称。

子类无法“完美继承”父类的私有字段(即以 __ 开头的属性),因为 Python 的名称重整(name mangling)机制会将 self.__attr 自动重命名为 self._ClassName__attr,使其在子类中不可直接访问——这不是限制访问权限,而是避免命名冲突的设计。所谓“确保完美继承”,实际是指:让子类能安全、规范、可维护地读写这些受控字段。关键不在于绕过私有机制,而在于通过父类暴露的接口来协作。
用 @property 和 @setter 封装私有字段
这是最推荐的方式。把私有字段藏在属性装饰器背后,子类调用的是公开、语义清晰的属性名,无需关心底层存储形式。
示例:
父类定义带验证的私有字段
class Person:
def __init__(self, name):
self.__name = name # 真实私有字段
<pre class='brush:php;toolbar:false;'>@property
def name(self):
return self.__name
@name.setter
def name(self, value):
if not isinstance(value, str) or not value.strip():
raise ValueError("姓名必须是非空字符串")
self.__name = value.strip()
子类直接使用 name 属性,无需改动逻辑
class Student(Person):
def __init__(self, name, student_id):
super().__init__(name)
self.student_id = student_id
<pre class='brush:php;toolbar:false;'>def introduce(self):
return f"我是学生 {self.name},学号 {self.student_id}"使用
s = Student(" 张三 ", "S1001") print(s.name) # → "张三"(自动去空格) s.name = "李四" # ✅ 走 setter,自动校验
在子类中扩展或定制访问逻辑
若子类需要对同一字段施加额外约束或行为,可重写 property 的 getter/setter,但需显式调用父类实现(避免重复逻辑)。
- 重写
@property时,用super().name获取原始值再加工 - 重写
@name.setter时,先做子类专属检查,再调用super(...).name.fset(self, value)或直接操作父类私有字段(不推荐) - 更稳妥的做法:在父类 setter 中预留钩子(如
_validate_name模板方法),子类覆盖该钩子
避免踩坑的实践要点
- 不要在子类中直接访问
self._Person__name—— 名称重整是实现细节,一旦父类名变更或 Python 版本升级,代码会悄无声息地失效 - 不要用单下划线
_name冒充“伪私有”来替代双下划线——它只是约定,不提供任何保护,也不表达设计意图 - 如果字段确实需被子类自由读写,就该定义为受保护字段(
_name)并配文档说明用途,而非强行用双下划线加 property 包裹 - 构造函数中初始化私有字段后,应立即通过 property 接口读取/验证一次,确保初始值合规
补充:为什么不用 __init__ 中直接暴露字段?
直接在 __init__ 中设 self.name = ... 并不能触发 @name.setter,会跳过所有校验和逻辑。正确做法是:在 __init__ 中调用 self.name = value(即走 setter),或在父类 __init__ 末尾主动调用一次 setter。

















