@property 仅控制读取,不拦截赋值;需配合@xxx.setter才能接管并验证赋值,如限制数值为正整数时在setter中校验并抛异常。

为什么直接用 @property 不能拦截赋值?
很多人以为加了 @property 就能“拦截所有访问”,其实它只控制读取(getter)。没定义 @xxx.setter 时,尝试赋值会直接报 AttributeError: can't set attribute —— 这不是拦截,是拒绝。
真正要实现“拦截”,得显式提供 setter,并在里面插入校验、日志或转换逻辑。
-
@property本身不带拦截能力,只是把方法伪装成属性读取 - 必须配对写
@xxx.setter才能接管赋值行为 - 如果只写
@property不写setter,属性默认只读
如何用 @property + @xxx.setter 拦截并验证赋值?
典型场景:限制某个数值属性只能为正整数。关键是在 setter 中做判断,不符合就抛异常或静默处理。
class Temperature:
def __init__(self):
self._celsius = 0
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if not isinstance(value, (int, float)):
raise TypeError("celsius must be a number")
if value
- 底层存储用私有变量
_celsius,避免循环调用 -
setter方法名必须和property名一致(这里是celsius) - 异常类型选
TypeError或ValueError更符合 Python 惯例,别用Exception
什么时候该用 __setattr__ 而不是 @property?
当你要统一拦截**所有属性**的赋值(比如打日志、禁止新增属性、做全局类型检查),@property 就不够用了——它只能逐个定义,没法泛化。
__setattr__ 是更底层的钩子,但要注意:直接给 self.xxx = yyy 会再次触发它,导致无限递归。
- 必须用
super().__setattr__('name', value)或object.__setattr__(self, 'name', value)绕过自身 - 对特定属性做拦截时,仍推荐
@property,语义清晰、调试友好 -
__setattr__适合框架层或需要强约束的配置类,普通业务类容易过度设计
常见陷阱:缓存失效和线程安全被忽略
如果在 getter 中做了耗时计算(比如读文件、查数据库),你可能加了缓存逻辑;但一旦 setter 修改了底层数据,缓存却没清,下次读就是脏数据。
另外,@property 本身不提供线程安全——多个线程同时读写同一属性,setter 里的校验可能失效(比如两个线程都通过了非空检查,然后都赋了值)。
- 有缓存就得在
setter里主动清理对应缓存(例如delattr(self, '_cached_result')) - 高并发场景下,别依赖
setter做原子性保证,该加锁还得加锁 - 别在
getter或setter里做阻塞 I/O,会拖慢整个属性访问
实际写的时候,先想清楚:这真是需要拦截的业务规则,还是只是图方便把逻辑塞进了属性?后者往往让代码更难测、更难改。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











