返回介绍

20.4 描述符用法建议

发布于 2024-02-05 21:59:47 字数 1892 浏览 0 评论 0 收藏 0

下面根据刚刚论述的描述符特征给出一些实用的结论。

使用特性以保持简单

内置的 property 类创建的其实是覆盖型描述符,__set__ 方法和 __get__ 方法都实现了,即便不定义设值方法也是如此。特性的 __set__ 方法默认抛出 AttributeError: can't set attribute,因此创建只读属性最简单的方式是使用特性,这能避免下一条所述的问题。

只读描述符必须有 __set__ 方法

如果使用描述符类实现只读属性,要记住,__get__ 和 __set__ 两个方法必须都定义,否则,实例的同名属性会遮盖描述符。只读属性的 __set__ 方法只需抛出 AttributeError 异常,并提供合适的错误消息。6

6Python 为此类异常提供的错误消息不一致。如果试图修改 complex 的 c.real 属性,那么得到的错误消息是 AttributeError: read-only attribute;但是,如果试图修改 c.conjugat(e complex 对象的方法),那么得到的错误消息是 AttributeError: 'complex' object attribute 'conjugate' is read-only。

用于验证的描述符可以只有 __set__ 方法

对仅用于验证的描述符来说,__set__ 方法应该检查 value 参数获得的值,如果有效,使用描述符实例的名称为键,直接在实例的 __dict__ 属性中设置。这样,从实例中读取同名属性的速度很快,因为不用经过 __get__ 方法处理。参见示例 20-1 中的代码。

仅有 __get__ 方法的描述符可以实现高效缓存

如果只编写了 __get__ 方法,那么创建的是非覆盖型描述符。这种描述符可用于执行某些耗费资源的计算,然后为实例设置同名属性,缓存结果。同名实例属性会遮盖描述符,因此后续访问会直接从实例的 __dict__ 属性中获取值,而不会再触发描述符的 __get__ 方法。

非特殊的方法可以被实例属性遮盖

由于函数和方法只实现了 __get__ 方法,它们不会处理同名实例属性的赋值操作。因此,像 my_obj.the_method = 7 这样简单赋值之后,后续通过该实例访问 the_method 得到的是数字 7——但是不影响类或其他实例。然而,特殊方法不受这个问题的影响。解释器只会在类中寻找特殊的方法,也就是说,repr(x) 执行的其实是 x.__class__.__repr__(x),因此 x 的 __repr__ 属性对 repr(x) 方法调用没有影响。出于同样的原因,实例的 __getattr__ 属性不会破坏常规的属性访问规则。

实例的非特殊方法可以被轻松地覆盖,这听起来不可靠且容易出错,可是在我使用 Python 的 15 年中从未受此困扰。然而,如果要创建大量动态属性,属性名称从不受自己控制的数据中获取(像本章前面那样),那么你应该知道这种行为;或许你还可以实现某种机制,过滤或转义动态属性的名称,以维持数据的健全性。

 示例 19-6 中的 FrozenJSON 类不会出现实例属性遮盖方法的问题,因为那个类只有几个特殊方法和一个 build 类方法。只要通过类访问,类方法就是安全的,在示例 19-6 中我就是这么调用 FrozenJSON.build 方法的——在示例 19-7 中替换成 __new__ 方法了。Record 类(见示例 19-9 和示例 19-11)及其子类也是安全的,因为只用到了特殊的方法、类方法、静态方法和特性。特性是数据描述符,因此不能被实例属性覆盖。

讨论特性时讲了两个功能,这里讨论的描述符还未涉及,结束本章之前我们来讲讲:文档和对删除托管属性的处理。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文