电话
400 9058 355
描述符类必须实现__set__和__delete__方法才能支持赋值与删除;仅含__get__为只读描述符。其必须作为类变量实例化(如attr = MyDescriptor()),且__set__需存值到WeakKeyDictionary或实例__dict__中以隔离状态,避免共享或内存泄漏。
__set__ 和 __delete__ 方法仅定义 __get__ 的类是只读描述符;要支持赋值和删除,__set__(self, obj, value) 和 __delete__(self, obj) 二者缺一不可。Python 在对实例属性赋值时,若发现同名类属性是描述符且实现了 __set__,就会跳过实例字典、直接调用该方法——这是描述符生效的前提。
常见错误是漏写 __set__ 却以为 @property 那套逻辑能复用;描述符协议和 @property 无关,后者只是内置描述符的语法糖,不能直接混用。
__set__ 必须接收三个参数:描述符实例、托管对象(即使用该属性的实例)、待赋的值__delete__ 必须接收两个参数:描述符实例、托管对象__set__,对该属性赋值会直接在实例上新建同名属性,覆盖描述符——此时 __get__ 后续也不再被调用描述符必须作为类变量(class variable)存在

attr = MyDescriptor() 是对的,写成 attr = MyDescriptor(没调用)或 attr = MyDescriptor() 放在 __init__ 里,都会失效。
典型误用场景:想给每个实例配独立描述符,结果把描述符实例化放到了 __init__ 中——这会让它变成普通实例属性,完全不触发描述符协议。
class MyClass:
attr = MyDescriptor() # 类变量,实例化一次class MyClass:
def __init__(self):
self.attr = MyDescriptor() # 实例属性,不触发协议class MyClass:
attr = MyDescriptor # 没调用,只是类引用描述符本身不自动绑定到实例数据;你得决定值存哪:可以存在描述符实例里(共享)、实例的 __dict__ 里(隔离)、或用 weakref.WeakKeyDictionary 做映射。多数情况推荐后者,避免循环引用和内存泄漏。
如果把值直接存进描述符自己的属性(如 self._value),那所有托管实例将共享这个值——这通常不是想要的行为。
weakref.WeakKeyDictionary 映射实例 → 值,确保实例销毁后自动清理__dict__,但需约定键名(如 _mydescriptor_attr),防止命名冲突__set__ 中对 None 或非法值的处理时机描述符的 __set__ 是唯一可控的赋值入口,也是做类型校验、范围限制、事件通知的最佳位置。但要注意:如果在 __set__ 中抛异常(如 ValueError),赋值操作会立即失败,不会写入任何地方——这点比 @property 更严格,也更可靠。
容易忽略的是 del obj.attr 触发 __delete__ 后,若后续又访问 obj.attr,__get__ 仍会被调用,但此时底层存储可能已清空。你需要在 __get__ 中判断值是否存在,否则可能抛 AttributeError 或返回意外默认值。
__set__ 中校验并规范化输入值(例如转为 int、截断字符串)__delete__ 中清除对应存储,不要留空洞状态__get__ 开头检查值是否存在,缺失时明确抛错或返回 sentinel(如 NotImplemented),别依赖隐式行为描述符的 setter/deleter 看似只是多写两个方法,实际难点在于存储策略的选择和生命周期管理——特别是跨实例状态隔离与自动清理,这里出错往往没有明显报错,而是数据静默错乱。
邮箱:8955556@qq.com
Q Q:8955556
本文详解如何将Go官方present工具(用于生成HTML5...
PySNMP在不同版本中对SNMP错误状态(errorSta...
time.Sleep仅阻塞当前goroutine,其他gor...
PHPfopen()创建含特殊符号的文件名失败主因是操作系统...
WooCommerce中通过代码为分组产品动态聚合子商品的属...
io.ReadFull返回io.ErrUnexpectedE...
本文详解Yii2中控制器向视图传递ActiveRecord数...
本文详解为何通过wp_set_object_terms()为...
Pytest中使用@mock.patch类装饰器会导致补丁泄...
带缓冲的channel是并发安全的FIFO队列;make(c...