TTL-кеш хранит результат и время истечения для каждого ключа аргументов. При повторном вызове до истечения возвращается кеш, после — функция вычисляется заново.
Задача: написать декоратор, который кэширует результат выполнения функции на определенное время (ttl). TTL задается в секундах (int).
TTL-кеш хранит результат и время истечения для каждого ключа аргументов. При повторном вызове до истечения возвращается кеш, после — функция вычисляется заново.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Задача: написать декоратор, который кэширует результат выполнения функции на определенное время (ttl). TTL задается в секундах (int).
- описать принципиальную схему работы этого кода со всеми возможными корнер-кейсами.
- (опционально) реализовать в коде
Ответ
TTL-кеш хранит результат и время истечения для каждого ключа аргументов. При повторном вызове до истечения возвращается кеш, после — функция вычисляется заново.
Минимальный вариант для синхронной функции, хешируемых аргументов и одного потока:
from functools import wraps
from time import monotonic
def ttl_cache(ttl: int):
if type(ttl) is not int or ttl <= 0:
raise ValueError('ttl must be a positive integer')
def decorate(fn):
cache = {}
@wraps(fn)
def wrapped(*args, **kwargs):
key = (args, tuple(sorted(kwargs.items())))
entry = cache.get(key)
if entry is not None and monotonic() < entry[0]:
return entry[1]
value = fn(*args, **kwargs)
cache[key] = (monotonic() + ttl, value)
return value
return wrapped
return decorate
TTL отсчитывается от завершения вычисления; ошибки не кешируются. Для production нужны ограничение размера, удаление просроченных записей и защита от одновременного вычисления. Эквивалентные позиционные и именованные вызовы здесь могут иметь разные ключи; изменяемые результаты возвращаются по ссылке.