[英]Difference between @cached_property and @lru_cache decorator
I am new to Django.我是 Django 的新手。 Would be really helpful if someone can tell the difference between @cached_property and @lru_cache decorator in Django.
如果有人能说出 Django 中 @cached_property 和 @lru_cache 装饰器之间的区别,那将非常有帮助。 Also when should I use which decorator in Django.
另外我什么时候应该在 Django 中使用哪个装饰器。 Use cases would be really helpful.
用例真的很有帮助。 Thanks.
谢谢。
First and foremost, lru_cache
is a decorator provided by the Python language itself as of version 3.4;首先,
lru_cache
是 Python 3.4 语言本身提供的装饰器; cached_property
is a decorator provided by Django for many years, while only being added to the Python language in version 3.8 in October 2019. That being said, they are similar. cached_property
是 Django 提供多年的装饰器,2019 年 10 月才在 Python 3.8 版本中添加。话虽如此,它们还是相似的。
lru_cache
is specifically useful in functional programming. lru_cache
在函数式编程中特别有用。 What it does is saves the results of function calls with a certain set of parameters.它的作用是用一组特定的参数保存函数调用的结果。 When a function decorated with
lru_cache
is called multiple times with the same parameters, the decorator will just return a cached result of the function result.当使用
lru_cache
装饰的函数以相同的参数被多次调用时,装饰器只会返回函数结果的缓存结果。 This employs a method of programming called dynamic programming , and more specifically, memoization .这采用了一种称为动态编程的编程方法,更具体地说,是记忆化。 Using these methods, you can drastically speed up code which repeatedly calls functions that are computationally expensive.
使用这些方法,您可以极大地加速重复调用计算成本高的函数的代码。
Python also provides another similar decorator called lfu_cache
. Python 还提供了另一个类似的装饰器,称为
lfu_cache
。 Both of these decorators accomplish memoization, however with different replacement policies .这两个装饰器都实现了记忆化,但是具有不同的替换策略。
lru_cache
(least recently used) will fill it's cache and have to kick something out during the next decorated function call. lru_cache
(最近最少使用)将填充它的缓存,并且必须在下一次装饰函数调用期间踢出一些东西。 This replacement policy dictates that the least recently used entry gets replaced by the new data.此替换策略规定最近最少使用的条目被新数据替换。
lfu_cache
(least frequently used) dictates that replacements happen based on which entries are used the least. lfu_cache
(最不常用)指示根据哪些条目使用最少来进行替换。
cached_property
is similar to lru_cache
in the sense that it caches the result of expensive function calls. cached_property
类似于lru_cache
,因为它昂贵缓存函数调用的结果的意义。 The only difference here is that it can only be used on methods , meaning the functions belong to an object.这里唯一的区别是它只能用于方法,这意味着函数属于一个对象。 Furthermore, they can only be used on methods that have no other parameters aside from
self
.此外,它们只能用于除了
self
之外没有其他参数的方法。 You would specifically want to use this during django development for a method on a class that hits the database.您会特别希望在 Django 开发期间将它用于访问数据库的类上的方法。 The Django docs mention its usage on a model class which has a property method
friends
. Django 文档提到了它在具有属性方法
friends
的模型类上的用法。 This method presumably hits the database to gather a set of people who are friends of that instance of Person
.该方法可能会访问数据库以收集一组
Person
实例的朋友。 Because calls to the database are expensive, we'd want to cache that result for later use.因为对数据库的调用很昂贵,所以我们希望缓存该结果以供以后使用。
They serve different purposes.它们用于不同的目的。
lru_cache
saves the least recent uses - you should specify maxsize
which distinguishes how many computations of your function you can save. lru_cache
保存最近最少的使用 - 您应该指定maxsize
来区分您可以保存多少函数的计算。 Once you surpass this number, the 'oldest' result is discarded and the new one is saved.一旦超过这个数字,“最旧”的结果就会被丢弃,而新的结果会被保存。
cached_property
just computes the result and saves it. cached_property
只是计算结果并保存它。 It doesn't take arguments unlike lru_cache
(you can think of it as a lru_cache
on an object type with maxsize = 1 with no arguments).与
lru_cache
不同,它不接受参数(您可以将其视为 maxsize = 1 且没有参数的对象类型上的lru_cache
)。
lru_cache
will keep alive the objects in the cache, which might lead to memory leak, especially if the instance in which the lru_cache is applied is big (see: https://bugs.python.org/issue19859 )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
With cached_property
, there is no cache, so no memory leak.使用
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
are read-only while @cached_property
are not:@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
This is due to the fact that @cached_property
are replacing the attribute, so the second call to bx
bypass the Bxget
descriptor call.这是因为
@cached_property
正在替换属性,所以第二次调用bx
绕过了Bxget
描述符调用。
cached_property
is more performant if you access the same attribute multiple times, while lru_cache
has overhead for the function call and attribute lookup.cached_property
的性能会更高,而lru_cache
有函数调用和属性查找的开销。 Note the difference is only visible with huge numbers.[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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.