8.11. Iteratory asynchroniczne¶
Iterator asynchroniczny to asyncio-wa wersja iteratora. W Przeglądzie Pythona przedstawiono synchroniczny protokół iteratora – __iter__ i __next__ – oraz pętlę for, która go napędza. Iteratory asynchroniczne dodają await do każdego kroku, dzięki czemu iterator może czekać pomiędzy wartościami bez blokowania pętli zdarzeń.
8.11.1. Protokół¶
Klasa jest iteratorem asynchronicznym, gdy implementuje
__aiter__(self)– zwraca obiekt iteratora, zazwyczajself.async def __anext__(self)– zwraca następną wartość lub zgłaszaStopAsyncIteration, gdy nie ma już więcej wartości.
Odpowiadającą jej konstrukcją pętli jest 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)
Pętla async for czeka (await) na każde wywołanie __anext__, więc korutyna napędzająca pętlę oddaje sterowanie pętli zdarzeń pomiędzy wartościami, dokładnie tak jak przy każdym innym await.
8.11.2. Kiedy z tego korzystać¶
Zawsze, gdy aplikacja ma źródło danych napływających w czasie, a konsument powinien iterować po nich leniwie. Sensor produkujący próbkę na okres, gniazdo sieciowe dostarczające komunikaty, generator ramek – wszędzie tam, gdzie pasuje schemat while True: x = await source.next(); process(x), czystszym zapisem jest async for x in source.
Dla źródeł emitujących dane aż do skończonego warunku końcowego iterator zgłasza StopAsyncIteration, a async for kończy się normalnie. Dla źródeł, które nigdy się nie kończą – ciągłego strumienia z sensora, długotrwałej subskrypcji – pętla działa do momentu anulowania korutyny, w której się znajduje, lub zburzenia jej zadania.