什么是惰性属性?

21 投票
1 回答
25304 浏览
提问于 2025-04-18 12:59

在网上查阅webapp2的文档时,我发现了一个装饰器的信息:webapp2.cached_property

文档中提到:

这是一个将函数转换为懒加载属性的装饰器。

我想问的是:

什么是懒加载属性?

1 个回答

38

这是一个 property 装饰器,它在第一次调用后就不再干扰。它可以让你自动缓存计算出来的值。

标准库中的 @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__ 属性的对象上使用,或者该对象不支持项赋值时,会产生更好的错误信息,并且它也是线程安全的。

撰写回答