这是一个属性装饰器,第一次调用后就不再起作用。它允许您自动缓存计算出的值。
标准库中的@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__
属性的对象上使用或该对象不支持项赋值,则生成更好的错误消息,并且也是线程安全的。
functools.cached_property
。 - martineau