什么是延迟属性?

20

在浏览 webapp2 文档时,我找到了关于装饰器的信息:webapp2.cached_property

文档中写道:

将函数转换为惰性属性的装饰器。

我的问题是:

什么是惰性属性?

1个回答

37

这是一个属性装饰器,第一次调用后就不再起作用。它允许您自动缓存计算出的值。

标准库中的@property装饰器是一个数据描述符对象,即使实例上有相同名称的属性,也会始终被调用。

另一方面,@cached_property装饰器只有一个__get__方法,这意味着如果已经存在同名的属性,则不会被调用。它通过在第一次调用时在实例上设置一个同名属性来利用这一点。

假设在名为foo的实例上有一个使用@cached_property装饰的bar方法,以下是发生的情况:

  • Python解析foo.bar。实例上没有找到bar属性。

  • Python在类上找到bar描述符,并调用其中的__get__方法。

  • cached_property__get__方法调用装饰的bar方法。

  • bar方法计算一些东西,并返回字符串'spam'

  • cached_property__get__方法获取返回值并在实例上设置一个新的bar属性;foo.bar = 'spam'

  • cached_property__get__方法返回'spam'的返回值。

  • 如果再次请求foo.bar,Python会在实例上找到bar属性,并从此使用它。

同时请参阅原始Werkzeug实现的源代码

# implementation detail: this property is implemented as non-data
# descriptor.  non-data descriptors are only invoked if there is
# no entry with the same name in the instance's __dict__.
# this allows us to completely get rid of the access function call
# overhead.  If one choses to invoke __get__ by hand the property
# will still work as expected because the lookup logic is replicated
# in __get__ for manual invocation.

请注意,从 Python 3.8 开始,标准库具有类似的对象@functools.cached_property()。它的实现稍微更加健壮,可以防止在不同名称下意外重复使用,如果在没有__dict__属性的对象上使用或该对象不支持项赋值,则生成更好的错误消息,并且也是线程安全的。


6
Python 3.8 中添加了 functools.cached_property - martineau
2
@martineau:是的,谢谢你提醒我关于那个答案。我已经在我的答案中添加了一个简短的介绍。 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接