简体   繁体   English

@cached_property 和 @lru_cache 装饰器的区别

[英]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 )。

  1. A major difference is that 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
  1. Another difference (which might be a bug) is that @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描述符调用。

  1. Another difference which likely don't matter in most cases is that 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.

相关问题 如何从 @cached_property 装饰器中清除缓存? - How do I clear the cache from @cached_property decorator? functool的缓存和lru_cache的区别 - Difference between functool's cache and lru_cache @lru_cache 装饰器过多的缓存未命中 - @lru_cache decorator excessive cache misses 用 lru_cache 装饰器测试 function - Test function with lru_cache decorator 如何合并数据类,属性和lru_cache - How to combine dataclass, property, and lru_cache 我的缓存函数抛出 TypeError(用 lru_cache 装饰) - My cached function throws TypeError (decorated with lru_cache) Django 中的 cached_property 与 Python 的 functools 有什么区别? - What is the difference between cached_property in Django vs. Python's functools? 在使用 lru_cache 装饰器时将不同的参数模式视为相同的调用 - Treat distinct argument patterns while using lru_cache decorator as the same calls 如何将SQLAlchemy的@hybrid_property装饰器与Werkzeug的cached_property装饰器结合在一起? - How to combine SQLAlchemy's @hybrid_property decorator with Werkzeug's cached_property decorator? 使用functools lru_cache,如何确定该函数是否返回了我缓存的数据? - Using functools lru_cache, how can I determine if the function returned me cached data?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM