Lru cache python none

functools.lru_cache

maxsize=128 — Максимальное хранимое количество результатов (читай: размер кеша). Если установить в ‘None’, то размер не будет ограничен (и кеш перестанет быть LRU). Лучше, если значение будет степенью двойки.

+py3.3. typed=False — Если ‘True’, то разные типы будут кешироваться отдельно: например, результаты ‘f(3)’ и ‘f(3.0)’ будут закешированы по отдельности.

LRU (least recently used) кеш — кеш с конечным размером, где часто используемые записи вытесняют прочие.

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

 from functools import lru_cache


@lru_cache(maxsize=30)
def get_articles(on_date):

# Здесь затратная операция, выбирающая статьи
# на определённую дату.
articles = [. ]

return articles


get_articles('2017-12-12')
get_articles('2017-12-13')
get_articles('2017-12-12') # Взяты из кеша.


# Рассмотрим эффективность кеша:
get_articles.cache_info()
# CacheInfo(hits=1, misses=2, maxsize=20, currsize=2)

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

Эту функцию декоратор прикрепит к декорируемой функции. При помощи неё можно замерять эффективность кеша.
Она возвращает именованный кортеж CacheInfo со следующими атрибутами:

hits Количество попаданий в кеш.
misses Количество промахов мимо кеша.
maxsize Максимальный размер кеша.
currsize Текущий размер кеша.

Эту функцию декоратор прикрепит к декорируемой функции. С её помощью можно очистить/инвалидировать кеш.

Этот атрибут декоратор прикрепит к декорируемой функции. В нём будет содержаться ссылка на оригинальную функцию, к которой был применён декоратор. Может быть полезно для интроспекции, обхода кеша, или для оборачивания в другой кеш.

Источник

Функции для кэширования функций (еще немного functools)

В прошлой статье мы говорили о полезных функциях functools. Сегодня рассмотрим функции кэширования lru_cache , cache , cached_property . Они вам могут пригодиться в тех случаях, когда ваши функции постоянно вычисляет одни и те же значения. Поэтому чтобы избежать повторных вычислений, вы можете добавить результаты вызова функций в кэше.

Читайте также:  All string methods in python

Функция lru_cache из functools

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

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

Продемонстрируем это на примере. Допустим мы написали рекурсивную функцию подсчета арифметической прогрессии. Вот так она выглядит:

def mysum(n): if n == 1: return n print(f"''", end=" ") return n + mysum(n - 1)

Мы специально добавили функцию print для вывода в стандартный поток вывода текущего значения n . Повызываем эту функцию с разными значениями:

>>> mysum(11) '11' '10' '9' '8' '7' '6' '5' '4' '3' '2' 66 >>> mysum(11) '11' '10' '9' '8' '7' '6' '5' '4' '3' '2' 66 >>> mysum(7) '7' '6' '5' '4' '3' '2' 28 >>> mysum(9) '9' '8' '7' '6' '5' '4' '3' '2' 45

Как видим, она постоянно вычисляет одни и те же значения n . Итак, чтобы избежать повторных вычислений мы можем сохранить в кэше результаты предыдущих вычислений с помощью lru_cache , добавив только одну строчку с декоратором:

import functools as ftl @ftl.lru_cache def mysum(n): if n == 1: return n print(f"''", end=" ") return n + mysum(n - 1) >>> mysum(11) '11' '10' '9' '8' '7' '6' '5' '4' '3' '2' 66 >>> mysum(11) 66 >>> mysum(9) 45 >>> mysum(7) 28 >>> mysum(15) '15' '14' '13' '12' 120

Поскольку результаты от sum(1) до sum(11) уже найдены и сохранены в кэше, то они из него достаются, поэтому mysum(15) вычисляет вызовы от 15 до 12.

Читайте также:  Python проверить имя файла

Как работает Least Recently Used (LRU) алгоритм. Параметры функции lru_cache

Алгоритм Least Recently Used (LRU) хранит maxsize самых последних используемых вызовов. Иными словами, вызовы, которые давно не используются выбрасываются из кэша, если он переполнен. Один из способов реализовать алгоритм LRU — это использовать двусвязный список из результатов (это параметры и вызовы функции) и отображений ключей на указатели списка. При добавлении ключа указатель на первый элемент списка меняется на указатель данного ключа. При этом элемент в списке с этим ключом удаляется, ведь он все равно станет первым. Хотя в Python алгоритм LRU устроен немного сложнее, например, он также учитывает свое использование в тредах [2].

Итак, до этого мы использовали lru_cache без явного задания параметров. Но на самом деле по умолчанию неявно передается maxsize=128 . Давайте зададим явно этот параметр, равным 3:

@ftl.lru_cache(maxsize=3) def mysum(n): if n == 1: return n print(f"''", end=" ") return n + mysum(n - 1) >>> mysum(10) '10' '9' '8' '7' '6' '5' '4' '3' '2' 55 >>> mysum(8) 36 >>> mysum(7) '7' '6' '5' '4' '3' '2' 28

Наша функция, теперь хранит только 3 последних вызова, т.е. значения 10, 9, 8 (не забывайте, что этой линейной нехвостовой рекурсии нужно возвратиться обратно, когда n = 1 ). А вот значения 7 и меньше в кэше не хранятся, поэтому функция вычисляется как и положено. С другой стороны, если мы захотим теперь снова вычислить mysum(10) , то вычислить нужно только mysum(8) , mysum(9) и mysum(10) :

Второй параметр декоратора lru_cache является typed , по умолчанию равный False . Если он равен True , то параметры декорируемой функции будут кэшированы отдельно. По умолчанию все параметры рассматриваются как эквивалентные. Это значит, что в некоторых случаях int может быть эквивалентен float ( 1 == 1.0 ) или список эквивалентен кортежу. В нашей рекурсивной функции даже передача True не гарантирует эквивалентности между int и float .

Читайте также:  Javascript dom document all

Также мы можем передать maxsize=None , это сделает кэш бесконечным. В Python 3.9 появилась функция cache , которая эквивалентна lru_cache(maxsize=None) .

Изучаем информацию о кэше

Кэшированные функции имеют метод cache_info , который выводит информацию о кэше:

>>> mysum.cache_info() CacheInfo(hits=2, misses=20, maxsize=3, currsize=3)
  • hits — количество сохраненных вызовов, что были извлечены из кэша. Действительно, вызвав функцию с параметром 8, мы достали из кэша значение mysum(8) ; с параметром 7, пришлось заново вычислять функцию; затем вызвав с параметром 10, мы достали из кэша значение mysum(7)
  • misses — количество раз когда, значение не было в кэше. Действительно, mysum(10) вычисляла все 10 значений, mysum(8) — просто достала значение из кэша, mysum(7) — вычисляла 7 значений, mysum(10) — вычисляла только значения 8, 9 и 10
  • maxsize — максимальное число значений, которые может хранить кэш
  • currsize — сколько значений занято в кэше на данный момент

Кэшируем геттеры класса с помощью cached_property

Еще одной невероятно полезной функцией является cached_property , которая предназначена для кэширования атрибутов класса. Если ваш геттер постоянно что-то вычисляет, то разумно было сохранять результаты. Например, в коде на Python ниже метод sumdata вызывает встроенную функцию sum . Если экземпляр постоянно обращается к этому методу, то после декорирования cached_property будет возвращаться значение из кэша.

class A: def __init__(self, data): self.data = data @ftl.cached_property def sumdata(self): return sum(self.data) >>> a = A([1, 2, 3, 4]) >>> a.sumdata 10 >>> a.sumdata # Заново не вычисляется 10

Источник

Оцените статью