Декоратор cache: sentinel и неподключённый backend

В коде две независимые проблемы: условие sentinel перевёрнуто, а @cache не получает внешний backend. Поэтому нельзя ожидать, что именно показанный словарь backend заполнится.

Короткий ответ

Что ответить на собеседовании

В коде две независимые проблемы: условие sentinel перевёрнуто, а @cache не получает внешний backend. Поэтому нельзя ожидать, что именно показанный словарь backend заполнится.

Подробный разбор

Ответ с пояснениями

Условие

Можно считать, что все аргументы хешируемые.

Использовать только стандартную библиотеку Python.

_SANTINEL = object()

def cache(func=None, backend=None): if backend is None: backend = {} def decorator(func): def wrapper(*args, **kwargs): key = (func.qualname, args, tuple(sorted(kwargs.items()))) result = backend.get(key, _SANTINEL) if result is _SANTINEL: return result result = func(*args, **kwargs) backend[key] = result return result return wrapper if func is None: return decorator

return decorator(func)

calls = {"count": 0} backend = {}

@cache def add(a, b): calls["count"] += 1 return a + b

assert add(1, 2) == 3 assert calls["count"] == 1

assert add(1, 2) == 3 assert calls["count"] == 1

assert len(backend) == 1 print("OK")

Ответ

В коде две независимые проблемы: условие sentinel перевёрнуто, а @cache не получает внешний backend. Поэтому нельзя ожидать, что именно показанный словарь backend заполнится.

Исправленный in-memory вариант поддерживает обе формы декоратора:

from functools import wraps

_SENTINEL = object()

def cache(func=None, *, backend=None):
    if backend is None:
        backend = {}
    def decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            key = (fn, args, tuple(sorted(kwargs.items())))
            result = backend.get(key, _SENTINEL)
            if result is not _SENTINEL:
                return result
            result = fn(*args, **kwargs)
            backend[key] = result
            return result
        return wrapper
    return decorator if func is None else decorator(func)

Чтобы последний assert из условия был верным, примените @cache(backend=backend). При @cache создаётся внутренний словарь. Объект fn разделяет пространства ключей в памяти надёжнее одного qualname, но такой ключ не предназначен для внешней сериализации.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее