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