Условие:
Рассмотрим следующий фрагмент кода с использованием
asyncio и декораторов:
import asyncio
def cache(func):
cache = {}
async def wrapper(*args):
if args not in cache:
cache[args] = await func(*args)
return cache[args]
return wrapper
@cache
async def expensive_operation(x):
await asyncio.sleep(1)
return x * 2
async def main():
tasks = [expensive_operation(i) for i in range(3)]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
Вопрос:
Что будет выведено в консоль и почему?
Варианты ответов:
1)
[0, 2, 4] — вывод корректный, кеширование работает 2)
RuntimeError — возникает из-за неправильного использования await в декораторе 3)
[0, 0, 0] — возникает ошибка кеширования 4)
[2, 2, 2] — декоратор мешает асинхронности Правильный ответ: 3)
Пояснение:
1️⃣ Проблема в декораторе: обычный
def-декоратор не совместим с асинхронными функциями. При вызове await func(args) внутри wrapper, декоратор не дожидается выполнения корутины, возвращая None. 2️⃣ Почему
[0, 0, 0]: —
cache[args] сохраняет не результат, а корутину (<coroutine object...>). — При повторном обращении к кешу возвращается эта корутина, но
await вне декоратора не применяется. — Фактически, все задачи получают первую сохраненную корутину (для x=0), которая всегда возвращает 0.
3️⃣ Чтобы исправить код нужно использовать
functools.wraps и сделать декоратор асинхронным. Пишите свои варианты решения в комментариях 🔽
@python_job_interview