@cached_property和@lru_cache装饰器

时间:2018-03-22 19:21:35

标签: python django

我是Django的新手。如果有人可以在Django中区分@cached_property和@lru_cache装饰器,那将会非常有用。 我什么时候应该在Django中使用哪个装饰器。用例非常有用。 谢谢。

3 个答案:

答案 0 :(得分:6)

首先,lru_cache是python语言本身提供的装饰器,自python 3.4开始; cached_property是一个专门从django提供的装饰器。话虽如此,它们是相似的。

lru_cache在函数式编程中特别有用。它的作用是使用一组参数保存函数调用的结果。当用lru_cache装饰的函数用相同的参数多次调用时,装饰器将只返回函数结果的缓存结果。这采用了一种名为dynamic programming的编程方法,更具体地说是memoization。使用这些方法,您可以大大加快重复调用计算成本高昂的函数的代码。

Python还提供了另一个名为lfu_cache的类似装饰器。这两个装饰器都实现了memoization,但是使用了不同的replacement policieslru_cache(最近最少使用的)将填充它的缓存,并且必须在下一个修饰函数调用期间踢出一些东西。此替换策略规定最近最少使用的条目将被新数据替换。 lfu_cache(最不常用)指示根据最少使用哪些条目进行替换。

cached_property 类似于lru_cache,因为它缓存了昂贵的函数调用的结果。这里唯一的区别是它只能用于方法,这意味着函数属于一个对象。此外,它们只能用于除self之外没有其他参数的方法。您特别希望在django开发期间使用此方法来获取命中数据库的类的方法。 Django docs在具有属性方法friends的模型类中提及它的用法。此方法可能会命中数据库以收集一组作为该Person实例的朋友的人。因为对数据库的调用很昂贵,所以我们希望缓存该结果以供以后使用。

答案 1 :(得分:2)

  1. 主要区别在于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
  1. 另一个差异(可能是错误)是@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描述符调用。

  1. 在大多数情况下,另一个可能无关紧要的区别是,如果多次访问同一个属性,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且没有参数)。