Кэширование Python: декоратор с таймаутом простоя
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Декораторы Python: задачи на retry, rate limiter, кэш и Circuit Breaker.
Кэш с тайм-аутом простоя
Справочник курсов обмена валют непрерывно обслуживает торговые терминалы, отвечая на частые запросы котировок. Чтобы не перегружать внешнюю биржу повторными обращениями, вызовы функции защищают параметризованным декоратором: он сохраняет переданные аргументы и полученные результаты в словаре кэша, а также отслеживает метку времени последнего обращения через монотонные часы.
Логика обновления опирается на общую активность терминалов. Если интервал между предыдущим любым вызовом функции и текущим моментом строго превысил заданный тайм-аут простоя, декоратор полностью очищает накопленный словарь кэша прямо перед обработкой нового запроса, после чего обновляет метку активности. Если же допустимый интервал не превышен, кэш сохраняется в прежнем виде, а при обнаружении готового значения в словаре возвращает его и точно так же отмечает время текущего визита.
Реализуйте такой декоратор и определите, какие ответы получат терминалы при заданной последовательности запросов котировок.
Что уже дано
В редакторе представлена заготовка функции idle_cache(timeout, clock). Вспомогательные классы и проверочные функции доступны при запуске тестов, реализовывать их заново не требуется.
Что нужно сделать
Реализуйте фабрику декоратора idle_cache(timeout, clock).
Параметры фабрики:
timeout— неотрицательное конечное число (intилиfloat, включая числовые подклассы) в диапазоне от0до1 000 000секунд;clock— вызываемый объект без аргументов, возвращающий текущее монотонное время в секундах (intилиfloatот0до1 000 000 000). Часы не выбрасывают исключений, а их показания не убывают.
Фабрика возвращает декоратор, принимающий синхронную целевую функцию и возвращающий функцию-обёртку.
Правила работы обёртки:
- Время поступления текущего запроса считывается через вызов
clock()в самом начале обработки, строго до возможного вызова целевой функции. Никаких пауз (sleep) и ожидания реального времени выполнять не нужно. - Интервал неактивности измеряется от момента поступления предыдущего любого вызова этой же обёртки (независимо от того, попал ли прошлый вызов в кэш или завершился ошибкой). При самом первом вызове предыдущего визита нет, а кэш изначально пуст.
- Если разница
current_time - last_arrival_timeстрого большеtimeout(> timeout), весь накопленный кэш полностью очищается перед обработкой текущего запроса. Если разница меньше либо равнаtimeout, кэш сохраняется. - Время последнего визита обновляется на текущее считанное время для каждого входящего вызова.
- Ключ кэширования формируется строго по правилу урока:
(args, tuple(sorted(kwargs.items()))). Разный порядок передачи именованных аргументов формирует один и тот же ключ. Позиционные и именованные аргументы не приводятся друг к другу. - Если для сформированного ключа в актуальном кэше уже есть значение (попадание), обёртка возвращает сохранённый объект без вызова целевой функции.
- Если значения в кэше нет (промах), вызывается целевая функция с исходными аргументами
*argsи**kwargs. При успешном завершении возвращённый объект сохраняется в кэше и возвращается вызывающему коду. Ложные значения (False,None,0, пустые коллекции) считаются полноценными результатами и кэшируются на общих основаниях. - Если целевая функция выбросила исключение, оно беспрепятственно пробрасывается дальше и не сохраняется в кэше.
- Состояние кэша и времени последнего вызова должно быть строго изолированным: декорирование нескольких разных функций одним и тем же декоратором не приводит к разделению их кэша или отметок времени.
Ввод и вывод
Стандартные потоки stdin и stdout не используются: считывать ввод и вызывать print() не требуется. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня idle_cache и вызывает её с проверочными аргументами.
Пример использования
now=[0.0]
calls=[]
@idle_cache(5,lambda:now[0])
def quote(code):
calls.append(code)
return len(calls)
print(quote("EUR"))
now[0]=5
print(quote("EUR"))
now[0]=10
print(quote("EUR"))
now[0]=16
print(quote("EUR"))
print(calls)
Вывод:
1
1
1
2
['EUR', 'EUR']
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Functools.wraps в Python: имя функции и атрибут wrapped
- Декоратор Circuit Breaker на Python: остановка вызовов после сбоев
- Декораторы Python: проверка цепочки wrapped и метаданных функции
- Декоратор Python: логирование только медленных вызовов
- Кэширование Python: постройте ключ из выбранных аргументов функции
- Rate limiter на Python: декоратор со скользящим окном запросов
