直接赋值绕过@property逻辑是因为未定义setter方法,Python将其视为只读属性;需用同名@attr.setter显式定义并校验,且内部赋值须操作底层变量(如_self._x_)避免递归。

为什么直接赋值会绕过@property的逻辑
当你给一个被 @property 修饰的属性赋值时,Python 默认会报 AttributeError: can't set attribute。这不是 bug,而是因为只定义了 getter 没有 setter——Python 把它当成了只读属性。
要让 obj.attr = value 生效,必须显式定义对应的 setter 方法,且名字必须和 getter 一致(即同名函数),再用 @attr.setter 装饰。
- getter 函数名就是属性名,比如
age;setter 必须叫age,不能叫set_age - setter 方法必须接收一个额外参数(通常是
value),代表右侧赋的值 - 如果 getter 中做了类型检查或计算,setter 里通常也要做对应校验,否则可能破坏对象一致性
@property 和 @xxx.setter 必须在同一个类中定义
不能把 getter 写在父类、setter 写在子类里试图“覆盖”——Python 不支持跨类拆分同一个属性的访问器。子类中重定义 @xxx.setter 会覆盖父类的整个 property(包括 getter),除非你手动重新调用父类的 getter。
常见误写:
立即学习“Python免费学习笔记(深入)”;
class Parent:
@property
def name(self):
return self._name
<p>class Child(Parent):
@Parent.name.setter # ❌ 错误:不能这样引用父类的 property
def name(self, value):
self._name = value.upper()
正确做法是子类里完整重写:@property + @name.setter,并在 getter 中调用 super().name(如果需要复用逻辑)。
setter 中修改其他属性时要注意循环触发
如果 setter 里直接修改了触发该 setter 的属性本身,会导致无限递归。例如在 @x.setter 中写了 self.x = value * 2,就会反复调用自己。
- 所有内部赋值都应操作底层存储变量(如
self._x),而不是属性名self.x - 如果业务逻辑需要联动更新(比如改
width同时更新area),确保被更新的是普通变量或另一个带保护的 property,避免回环 - 调试时遇到
RecursionError: maximum recursion depth exceeded,第一反应就该查 setter 是否误用了属性名赋值
property 的性能开销和替代方案
@property 本质是运行时调用方法,比直接访问实例变量慢一点。对高频访问的简单字段(比如纯数据容器),没必要套一层 property。
但如果你需要以下任一能力,@property 就不可替代:
- 赋值时做类型/范围校验(比如限制
score在 0–100) - 延迟计算(比如
@property返回一个耗时构造的对象) - 兼容旧接口:原来用属性,后来想加逻辑,又不能改调用方代码
注意:property 不支持 __slots__ 优化,如果类大量实例化且内存敏感,得权衡是否值得用。
真正容易被忽略的是:property 的 getter 和 setter 是独立绑定的——你可以只设 getter 让属性“可读不可写”,也可以只设 setter(虽然少见),甚至用 @xxx.deleter 控制 del obj.xxx 行为。别默认以为“有 getter 就一定有 setter”。


















