@property不可复用且不支持参数化校验;描述符通过__get__/__set__/__delete__实现可复用约束,__set_name__确保字段名准确,与__slots__混用需额外处理存储机制。

为什么直接用 @property 不够用?
因为 @property 只能绑定到具体类的实例属性上,无法复用;一旦多个字段都要做类型检查或范围校验,就得写一堆重复的 @xxx.setter。更麻烦的是,它不能自动作用于子类新增的同名属性,也不支持参数化(比如想让不同字段用不同最小值)。描述符协议则把校验逻辑抽成独立对象,只要定义一次,就能在任意类里通过赋值语句触发——这才是真正可复用的数据约束机制。
怎么写一个带类型和范围校验的描述符?
关键在于实现 __get__、__set__ 和 __delete__ 方法,其中 __set__ 是校验入口。下面这个 IntField 支持指定最小值、最大值,并拒绝非整数:
__set_name__
- 没实现
__set_name__→self.name是None→getattr(instance, '_None')报AttributeError - 手动传名(如
IntField('age', ...))→ 类变量名改了但构造参数没同步 → 校验提示和实际字段脱节
描述符和 __slots__ 一起用会出什么问题?
如果类启用了 __slots__,实例不再有 __dict__,而上面示例依赖 setattr(instance, f'_{self.name}', ...) 往实例上塞私有属性——这在 __slots__ 下会失败,抛出 AttributeError: 'Person' object has no attribute '_age'。解决办法是:要么在 __slots__ 里显式加上 '_age' 这类内部字段名,要么改用弱引用字典(weakref.WeakKeyDictionary)在描述符内部存储值。
后者更干净,但要注意:弱引用字典只存活跃实例,一旦实例被回收,对应校验值就没了;前者要维护两处字段名(__slots__ 和描述符名),稍不一致就崩。实际项目中,建议优先避开 __slots__ 和描述符混用,除非真有内存敏感场景。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











