8.11. Asynchrone Iteratoren

Ein asynchroner Iterator ist die asyncio-Variante eines Iterators. Die Python-Übersicht hat das synchrone Iterator-Protokoll eingeführt – __iter__ und __next__ – sowie die for-Schleife, die es antreibt. Asynchrone Iteratoren fügen jedem Schritt ein await hinzu, sodass der Iterator zwischen den Werten warten kann, ohne die Ereignisschleife zu blockieren.

8.11.1. Das Protokoll

Eine Klasse ist ein asynchroner Iterator, wenn sie Folgendes implementiert

  • __aiter__(self) – gibt das Iterator-Objekt zurück, normalerweise self.

  • async def __anext__(self) – gibt den nächsten Wert zurück oder löst StopAsyncIteration aus, wenn keine weiteren Werte vorhanden sind.

Das zugehörige Schleifenkonstrukt ist 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)

Die async for-Schleife erwartet jeden Aufruf von __anext__, sodass die Koroutine, die die Schleife ausführt, zwischen den Werten die Kontrolle an die Ereignisschleife abgibt – genau wie bei jedem anderen await.

8.11.2. Wann man darauf zurückgreift

Immer dann, wenn die Anwendung eine Datenquelle hat, deren Daten im Laufe der Zeit eintreffen und vom Konsumenten verzögert (lazy) iteriert werden sollen. Ein Sensor, der pro Periode eine Probe liefert, ein Netzwerk-Socket, der Nachrichten zustellt, ein Einzelbild-Generator – überall dort, wo die Form while True: x = await source.next(); process(x) passt, ist async for x in source die elegantere Schreibweise.

Bei Quellen, die bis zu einer endlichen Abbruchbedingung Werte liefern, löst der Iterator StopAsyncIteration aus und async for wird normal beendet. Bei Quellen, die nie enden – ein kontinuierlicher Sensorstrom, ein langlaufendes Abonnement – läuft die Schleife so lange, bis die Koroutine, in der sie lebt, abgebrochen oder ihr Task abgebaut wird.