В коде две независимые проблемы: условие sentinel перевёрнуто, а @cache не получает внешний backend. Поэтому нельзя ожидать, что именно показанный словарь backend заполнится.
Декоратор cache: sentinel и неподключённый 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, но такой ключ не предназначен для внешней сериализации.