Короткий ответ
lru_cache сохраняет результаты вызовов функции по кортежу её аргументов и возвращает закешированный результат при повторном вызове с теми же аргументами, вытесняя наименее недавно использованные записи при превышении лимита maxsize; он опасен для функций с нехешируемыми или изменяемыми аргументами, для функций с побочными эффектами или зависимостью от внешнего состояния, а также может привести к удержанию в памяти больших объектов дольше, чем ожидается, особенно если декорировать метод экземпляра.
Как это работает подробнее
lru_cache строит внутренний словарь, где ключ — это представление позиционных и именованных аргументов вызова, а значение — результат функции; при повторном вызове с таким же ключом функция не выполняется повторно, а сразу возвращается сохранённый результат. Поскольку ключ строится на основе хеша аргументов, все аргументы должны быть хешируемыми (immutable), иначе будет TypeError.
Пример кода
from functools import lru_cache
@lru_cache(maxsize=256)
def fib(n: int) -> int:
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(30)) # быстро за счёт кеша повторных подвызовов
print(fib.cache_info()) # CacheInfo(hits=..., misses=..., maxsize=256, currsize=...)
try:
@lru_cache
def total(items): # list — нехешируемый аргумент
return sum(items)
total([1, 2, 3])
except TypeError as e:
print("Ошибка:", e)Что использовать на практике
- Практика. lru_cache хорошо подходит для чистых функций (детерминированных, без побочных эффектов), зависящих только от своих аргументов — классический пример, вычисление чего-то дорогого по небольшому диапазону входных значений. Декорирование метода экземпляра (
self.method) опасно:selfстановится частью ключа кеша, и кеш неявно держит ссылку на сам объектself, продлевая его жизнь дольше, чем ожидалось (утечка памяти), пока кеш не очищен явно (cache_clear()) или объект не исчезнет из всех ключей кеша.
Подводные камни
- Риски. Кешируют функцию, которая обращается к изменяемому внешнему состоянию (конфигурации, текущему времени, данным из БД), и получают устаревшие результаты после изменения этого состояния, так как кеш не инвалидируется автоматически. Также применяют
lru_cacheбез ограниченияmaxsize(бесконечный кеш) для функции с большим или неограниченным количеством уникальных аргументов, что приводит к неконтролируемому росту потребления памяти.