8.11. Iteratori asincroni¶
Un iteratore asincrono è la versione asyncio di un iteratore. La Panoramica di Python ha introdotto il protocollo sincrono degli iteratori – __iter__ e __next__ – e il ciclo for che lo guida. Gli iteratori asincroni aggiungono un await a ogni passo, così l’iteratore può attendere tra un valore e l’altro senza bloccare l’event loop.
8.11.1. Il protocollo¶
Una classe è un iteratore asincrono quando implementa
__aiter__(self)– restituisce l’oggetto iteratore, di solitoself.async def __anext__(self)– restituisce il valore successivo, oppure sollevaStopAsyncIterationquando non ce ne sono altri.
Il costrutto di ciclo corrispondente è 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)
Il ciclo async for attende ogni chiamata a __anext__, così la coroutine che esegue il ciclo cede il controllo all’event loop tra un valore e l’altro, esattamente come qualsiasi altro await.
8.11.2. Quando ricorrervi¶
Ogni volta che l’applicazione ha una sorgente di dati che arrivano nel tempo e il consumatore deve iterarli in modo pigro. Un sensore che produce un campione per periodo, un socket di rete che consegna messaggi, un generatore di frame – ovunque calzi la forma while True: x = await source.next(); process(x), async for x in source è la scrittura più pulita.
Per le sorgenti che emettono fino a una condizione di fine finita, l’iteratore solleva StopAsyncIteration e async for termina normalmente. Per le sorgenti che non finiscono mai – un flusso continuo da un sensore, una sottoscrizione di lunga durata – il ciclo viene eseguito finché la coroutine in cui vive non viene annullata o il suo task non viene smantellato.