什么是惰性属性?
在网上查阅webapp2的文档时,我发现了一个装饰器的信息:webapp2.cached_property
文档中提到:
这是一个将函数转换为懒加载属性的装饰器。
我想问的是:
→ 什么是懒加载属性?
1 个回答
这是一个 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__
属性的对象上使用,或者该对象不支持项赋值时,会产生更好的错误信息,并且它也是线程安全的。