8.11. Асинхронные итераторы¶
Асинхронный итератор – это asyncio-версия итератора. В разделе Обзор Python был представлен синхронный протокол итератора – __iter__ и __next__ – и цикл for, который им управляет. Асинхронные итераторы добавляют await на каждом шаге, так что итератор может ждать между значениями, не блокируя цикл событий.
8.11.1. Протокол¶
Класс является асинхронным итератором, когда он реализует
__aiter__(self)– возвращает объект итератора, обычноself.async def __anext__(self)– возвращает следующее значение или возбуждаетStopAsyncIteration, когда значений больше нет.
Соответствующая конструкция цикла – async for:
import asyncio
class FrameCounter:
"""Yield N integers, one per frame period."""
def __init__(self, n, period_ms):
self._n = n
self._period_ms = period_ms
self._i = 0
def __aiter__(self):
return self
async def __anext__(self):
if self._i >= self._n:
raise StopAsyncIteration
await asyncio.sleep_ms(self._period_ms)
self._i += 1
return self._i
async def main():
async for n in FrameCounter(5, 200):
print(n)
Цикл async for ожидает каждый вызов __anext__, поэтому корутина, выполняющая цикл, уступает управление циклу событий между значениями, как и при любом другом await.
8.11.2. Когда им пользоваться¶
Всякий раз, когда у приложения есть источник данных, поступающих со временем, и потребитель должен перебирать их лениво. Датчик, выдающий по одной выборке за период, сетевой сокет, доставляющий сообщения, генератор кадров – везде, где подходит форма while True: x = await source.next(); process(x), запись async for x in source является более чистым вариантом.
Для источников, выдающих данные до конечного условия завершения, итератор возбуждает StopAsyncIteration, и async for завершается штатно. Для источников, которые никогда не заканчиваются – непрерывный поток с датчика, долго работающая подписка – цикл выполняется до тех пор, пока не будет отменена корутина, в которой он находится, или не будет уничтожена её задача.