我是Django的新手。希望有人能告诉我@cached_property和@lru_cache装饰器在Django中的区别,以及在什么情况下应该使用哪个装饰器。使用案例会非常有帮助。谢谢。
我是Django的新手。希望有人能告诉我@cached_property和@lru_cache装饰器在Django中的区别,以及在什么情况下应该使用哪个装饰器。使用案例会非常有帮助。谢谢。
首先,lru_cache
是Python自带的装饰器,从3.4版本开始提供;cached_property
是Django多年来提供的装饰器,在2019年10月被添加到Python 3.8版本中。尽管如此,它们非常相似。
lru_cache
特别适用于函数式编程。它可以保存一组特定参数的函数调用结果。当装饰了 lru_cache
的函数被多次使用相同的参数调用时,该装饰器将返回一个缓存的函数结果。这使用了一种编程方法称为动态规划,更具体地说,是记忆化。使用这些方法,您可以极大地加速重复调用计算昂贵的函数的代码。
Python还提供了另一个类似的装饰器 called lfu_cache
。这两个装饰器都实现了记忆化,但采用不同的替换策略。 lru_cache
(最近最少使用)将填充缓存并在下一个装饰函数调用期间强制退出某些内容。替换策略规定最近最少使用的条目将被新数据替换。而lfu_cache
(最不经常使用)则根据哪些条目最少使用来进行替换。
cached_property
和 lru_cache
相似,它们都会缓存调用昂贵函数的结果。唯一的区别在于 cached_property
只能用于方法,也就是属于对象的函数。而且,这些方法只能有 self
作为参数,不能有其他参数。在 django 开发中,应该特别使用这个方法来处理需要访问数据库的类方法。 在 Django 文档 中,提到了将其应用于模型类上的属性方法 friends
,此方法预计会访问数据库以获取与该 Person
实例相对应的一组朋友。由于对数据库的调用很昂贵,因此我们希望将结果缓存以供以后使用。
cached_property
自 Python 3.8 起被包含在标准库中,它可以用来缓存类属性的值。 - Lord Elrondlru_cache
会保留缓存中的对象,这可能会导致内存泄露,特别是如果应用lru_cache
的实例很大(参见:https://bugs.python.org/issue19859)。class A:
@property
@functools.lru_cache(maxsize=None)
def x(self):
return 123
for _ in range(100):
A().x # Call lru_cache on 100 different `A` instances
# The instances of `A()` are never garbage-collected:
assert A.x.fget.cache_info().currsize == 100
使用 cached_property
,没有缓存,所以也不会有内存泄漏问题。
class B:
@functools.cached_property
def x(self):
return 123
b = B()
print(vars(b)) # {}
b.x
print(vars(b)) # {'x': 123}
del b # b is garbage-collected
@property
是只读的,而@cached_property
不是。cache_property
允许对属性进行写操作。查看Python文档A().x = 123
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
B().x = 123 # Works
这是因为 @cached_property
替换了属性,所以对 b.x
的第二次调用绕过了 B.x.get
描述符的调用。
cached_property
比 lru_cache
更高效,而 lru_cache
对于函数调用和属性查找有开销。注意,只有在处理大量数据时才会看到明显的差异。[A().x for _ in range(10_000)]
[B().x for _ in range(10_000)]
a = A()
b = B()
print(timeit.timeit(lambda: a.x, number=1_000_000)) # ~0.83
print(timeit.timeit(lambda: b.x, number=1_000_000)) # ~0.57
cached_property
允许对属性进行写操作。
来自Python文档的摘录:
cached_property()
的机制与property()
有些不同。常规属性会阻止属性写入,除非定义了setter。相比之下,cached_property
允许写入。 - PradeepKlru_cache
(最大缓存大小为1)是否正确地等同于并可替代cached_property
,只是cached_property
可能更高效?这种差异的相关性有多大?对于Python < 3.8来说,lru_cache
是适合模拟cached_property
的备选方法吗? - mara004@lru_cache
之上再添加一个额外的@property
,效果是一样的吗? - mara004@lru_cache
上面再添加一个@property
,效果是一样的吗? - undefined