Вызов lru_cache из functools

Известно, что в Python синтаксис:

@decorator
def function():
    return ...

аналогичен:

def function():
        return ...
function = decorator(function)

Но когда я попробовал реализовать подобное поведение с lru_cache из functools, он не сработал. Посмотрев повнимательнее, я увидел, что в сигнатуре вообще нет функции, которая принимается.

Теперь прошу объяснения, а каким образом мне вызвать lru_cache без использования спец. синтаксиса @ в Python?


Ответы (3 шт):

Автор решения: MaxU

Пример:

In [496]: def f(i):
     ...:     return i**2
     ...: 

In [497]: lru_cache()(f)(3)
Out[497]: 9

In [498]: lru_cache()(f)(4)
Out[498]: 16

Пояснение:

lru_cache возвращает функцию декоратор, которая ожидает на вход вашу функцию, которой в свою очередь нужно передать аргументы.

→ Ссылка
Автор решения: MarianD

Нет ни каких проблем, не понимаю, почему для вас «не сработало», на мой комментарий вы не ответили:

from functools import lru_cache

def f(x):
    return x * x

f = lru_cache(f)          # вместо @

for i in (1, 1, 1, 1, 2, 2):
    f(i)

print(f.cache_info())

Вывод:

CacheInfo(hits=4, misses=2, maxsize=128, currsize=2)
→ Ссылка
Автор решения: CrazyElf

Ну в общем-то понятно, почему у вас "не работает", хотя другие ответы на ваш вопрос тоже правильные.

def fib(n):     
    return 1 if n == 0 or n == 1 else fib(n-1) + fib(n-2)

Если вы обернёте функцию fib в декоратор, то имя fib получит кэшированная версия функции, поэтому кэшироваться будет как основной вызов функции fib, так и рекурсивные вызовы fib из самой себя.

А вот в случае, если вы вручную обернёте функцию fib в lru_cache и вызовете эту обёртку, то исходная функция fib так и останется обычной, не кэшированной. И все рекурсивные вызовы будут к обычной версии fib, от такого кэширования толку не будет, конечно.

cached_fib = lru_cache()(fib)
cached_fib(30)
print(cached_fib.cache_info())

Кэшированная функция вызвалась только один раз:

CacheInfo(hits=0, misses=1, maxsize=128, currsize=1)

Но! Это же питон. Вы можете присвоить кэшированную версию обратно в название исходной функции и всё будет кэшироваться:

fib = lru_cache()(fib)
fib(30)
print(fib.cache_info())

Кэш работает, потому что fib теперь кэшированная:

CacheInfo(hits=28, misses=31, maxsize=128, currsize=31)

Итак, по идее ваш код изначально должен был сработать с кэшированием, просто вы не нашли правильную форму вызова декоратора вручную.

→ Ссылка