我是Django的新手。如果有人可以在Django中区分@cached_property和@lru_cache装饰器,那将会非常有用。 我什么时候应该在Django中使用哪个装饰器。用例非常有用。 谢谢。
答案 0 :(得分:6)
首先,lru_cache
是python语言本身提供的装饰器,自python 3.4开始; cached_property
是一个专门从django提供的装饰器。话虽如此,它们是相似的。
lru_cache
在函数式编程中特别有用。它的作用是使用一组参数保存函数调用的结果。当用lru_cache
装饰的函数用相同的参数多次调用时,装饰器将只返回函数结果的缓存结果。这采用了一种名为dynamic programming的编程方法,更具体地说是memoization。使用这些方法,您可以大大加快重复调用计算成本高昂的函数的代码。
Python还提供了另一个名为lfu_cache
的类似装饰器。这两个装饰器都实现了memoization,但是使用了不同的replacement policies。 lru_cache
(最近最少使用的)将填充它的缓存,并且必须在下一个修饰函数调用期间踢出一些东西。此替换策略规定最近最少使用的条目将被新数据替换。 lfu_cache
(最不常用)指示根据最少使用哪些条目进行替换。
cached_property
类似于lru_cache
,因为它缓存了昂贵的函数调用的结果。这里唯一的区别是它只能用于方法,这意味着函数属于一个对象。此外,它们只能用于除self
之外没有其他参数的方法。您特别希望在django开发期间使用此方法来获取命中数据库的类的方法。 Django docs在具有属性方法friends
的模型类中提及它的用法。此方法可能会命中数据库以收集一组作为该Person
实例的朋友的人。因为对数据库的调用很昂贵,所以我们希望缓存该结果以供以后使用。
答案 1 :(得分:2)
lru_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
不是只读的: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
的调用和属性查找会有开销。请注意,只有在显示大量数字时,差异才可见。[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
答案 2 :(得分:1)
它们用于不同的目的。
lru_cache
保存最近的用途 - 您应指定maxsize
,以区分您可以保存的函数计算量。一旦超过这个数字,最老的&#39;结果被丢弃,新的结果被保存。
cached_property
只计算结果并保存。它没有采用与lru_cache
不同的参数(您可以将其视为对象类型的lru_cache
,其中maxsize = 1且没有参数)。