Ленивые итераторы (генераторы) с asyncio
У меня есть блокирующий, не асинхронный код, подобный этому:
def f():
def inner():
while True:
yield read()
return inner()
С помощью этого кода вызывающая сторона может выбрать, когда остановить функцию для генерации данных. Как изменить это на асинхронный? Это решение не работает:
async def f():
async def inner():
while True:
yield await coroutine_read()
return inner()
... так как yield
не может быть использован в async def
функции. Если я удалю async
от inner()
подпись, я не могу использовать await
больше.
1 ответ
Как отмечено выше, вы не можете использовать yield
внутри async
funcs. Если вы хотите создать генератор сопрограмм, вы должны сделать это вручную, используя __aiter__
а также __anext__
магические методы:
import asyncio
# `coroutine_read()` generates some data:
i = 0
async def coroutine_read():
global i
i += 1
await asyncio.sleep(i)
return i
# `f()` is asynchronous iterator.
# Since we don't raise `StopAsyncIteration`
# it works "like" `while True`, until we manually break.
class f:
async def __aiter__(self):
return self
async def __anext__(self):
return await coroutine_read()
# Use f() as asynchronous iterator with `async for`:
async def main():
async for i in f():
print(i)
if i >= 3:
break
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Выход:
1
2
3
[Finished in 6.2s]
Вам также может понравиться другой пост, где StopAsyncIteration
использует.
Upd:
Начиная с Python 3.6 у нас есть асинхронные генераторы и возможность использовать yield
прямо внутри сопрограмм.