#python #python-asyncio
Вопрос:
У меня есть ситуация, когда я хочу выйти из цикла async for. Мне удалось свести проблему к приведенному ниже приложению. Я ожидаю, что войду в раздел «наконец» контекстного менеджера при выходе из цикла в main. Другими словами, ожидаемый результат таков
пробовать
456
наконец
Выполнено
но что я получаю, так это
пробовать
456
Выполнено
наконец
а затем исключение, когда приложение закрывается.
Вот код
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def receiving():
try:
print('try')
yield 123
finally:
print('finally')
async def request_all():
async with receiving():
yield 456
async def main():
async for r in request_all():
print(r)
break
print('done')
asyncio.run(main())
Я нашел это сообщение об ошибке, которое кажется похожим, но, насколько я могу судить, оно было устранено до 3.8. Я проверил свою проблему на 3.8.2 и 3.9.6
Ответ №1:
Я вижу, как такое поведение сбивает с толку, но я не думаю, что это ошибка. Если вы обходите исчерпание асинхронного итератора с break
помощью , вы никогда не оставляете async with
вход request_all
, поэтому finally
-блок не будет выполнен до тех пор, пока цикл событий не завершится. Это имеет то преимущество, что вы можете исчерпать генератор позже.
Если вы уверены, что вам больше не нужен генератор, вы можете закрыть async_generator вместо break
ing, чтобы получить ожидаемое поведение:
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def receiving():
try:
print('try')
yield 123
finally:
print('finally')
async def request_all():
async with receiving():
yield 456
async def main():
gen = request_all()
async for r in gen:
print(r)
await gen.aclose() #instead of break
print('done')
asyncio.run(main())
Комментарии:
1. Спасибо! Это не очень интуитивно понятно, если вы спросите меня, но, по крайней мере, это не ошибка. Решение работает очень хорошо
2. Отказ от ответственности: Этот код предназначен для демонстрации того, как работает контекстный менеджер. Пожалуйста, не используйте его в производственном коде как есть 🙂
3. Просто слово предостережения: помните , что, кроме как с
break
,gen.aclose()
не выйдет изfor
цикла-сразу, и код после этого будет выполнен на последней итерации.