Сообщить об ошибке.

Декоратор @lru_cache() модуля functools в Python

Кеширование результата выполнения функции

Синтаксис:

from functools import lru_cache

cache = lru_cache(user_function)
# или 
@lru_cache(maxsize=128, typed=False)
def user_function():
    ...

Параметры:

Возвращаемое значение:

  • кэшируемый результат user_function.

Описание:

Декоратор @lru_cache() модуля functools оборачивает функцию с переданными в нее аргументами и запоминает возвращаемый результат соответствующий этим аргументам. Такое поведение может сэкономить время и ресурсы, когда дорогая или связанная с вводом/выводом функция периодически вызывается с одинаковыми аргументами.

Позиционные и ключевые аргументы, переданные в функцию должны быть хешируемыми, т.к. для кэширования результатов декоратор lru_cache() использует словарь.

Различные аргументы можно рассматривать как отдельные вызовы с отдельными записями в кэше. Например f(a=1, b=2) и f(b=2, a=1) различаются по порядку ключевых аргументов и могут иметь две отдельные записи в кэше.

Если указана user_function, то она должна быть вызываемой. Это позволяет применять декоратор @lru_cache непосредственно к пользовательской функции, оставляя значение maxsize по умолчанию равным 128:

from functools import lru_cache

@lru_cache
def count_vowels(sentence):
    sentence = sentence.casefold()
    return sum(sentence.count(vowel) for vowel in 'aeiou')

Если для параметра maxsize установлено значение None, функция LRU декоратора отключена и кэш может расти без ограничений. Функция LRU работает лучше всего, когда maxsize является степенью двойки.

Если для typed задано значение True, то аргументы функций разных типов будут кэшироваться отдельно. Например f(3) и f(3.0) будут рассматриваться как отдельные вызовы с разными результатами. Если введено typed=False, ТО реализация обычно будет рассматривать их как эквивалентные вызовы и кэшировать только один результат. (Некоторые типы, такие как str и int, могут кэшироваться отдельно, даже если передано False)

Обратите внимание: специфичность типа применяется только к непосредственным аргументам функции, а не к их содержимому. Скалярные аргументы Decimal(42) и Fraction(42) обрабатываются как отдельные вызовы с разными результатами. Напротив, аргументы кортежа ('answer', Decimal(42)) и ('answer', Fraction(42)) обрабатываются как эквивалентные.

Обернутая функция в этот декоратор будет иметь метод .cache_parameters() (Новое в версии 3.9), которая возвращает новый dict, показывающий значения maxsize и typed. Этот метод служит только для информационных целей. Изменение значений не имеет никакого эффекта.

Чтобы помочь измерить эффективность кэша и настроить параметр maxsize, декорированная функция получает метод .cache_info(), который возвращает именованный кортеж, показывающий hits, misses, maxsize и currsize. В многопоточной среде hits и misses являются приблизительными.

Декоратор также предоставляет метод .cache_clear() для очистки или аннулирования кэша.

Исходная базовая функция доступна через атрибут __wrapped__, что полезно для самоанализа, для обхода кеша или для преобразования функции в другой кеш.

Кэш LRU работает лучше всего, т.к. алгоритм кэширования LRU, при наличии установленного параметра maxsize, в первую очередь вытесняет неиспользованные дольше всех кэшированные значения. Ограничение размера кеша гарантирует, что кеш не будет расти без привязки к длительным процессам, таким как веб-серверы.

В общем случае кэш LRU следует использовать только тогда, когда вы хотите повторно использовать ранее вычисленные значения. Нет смысла кэшировать функции, которые должны создавать различные изменяемые объекты при каждом вызове или нечистые функции, такие как time.time() или random.random().

Примеры использования:

Пример кэша LRU для статического веб-контента:

from functools import lru_cache

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
    try:
        with urllib.request.urlopen(resource) as s:
            return s.read()
    except urllib.error.HTTPError:
        return 'Not Found'

>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
...     pep = get_pep(n)
...     print(n, len(pep))

>>> get_pep.cache_info()
# CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

Пример эффективного вычисления чисел Фибоначчи с использованием кэша для реализации техники динамического программирования:

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> [fib(n) for n in range(16)]
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> fib.cache_info()
# CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

Изменено в версии 3.8: Добавлена ​​опция user_function.

Новое в версии 3.9: Добавлен метод .cache_parameters().