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 するため、ループを実行しているコルーチンは、他の await と同様に、値の間でイベントループに制御を譲ります。
8.11.2. いつ使うべきか¶
アプリケーションに 時間とともに 到着するデータソースがあり、コンシューマがそれを遅延的に反復処理すべき場合にはいつでも使います。期間ごとにサンプルを生成するセンサー、メッセージを配信するネットワークソケット、フレームジェネレータなど、while True: x = await source.next(); process(x) という形が当てはまる場面では、async for x in source のほうがすっきりした書き方になります。
有限の終了条件まで値を発行するソースの場合、イテレータは StopAsyncIteration を送出し、async for は正常に終了します。決して終わらないソース(連続的なセンサーストリーム、長時間稼働するサブスクリプションなど)の場合、ループはそれを含むコルーチンがキャンセルされるか、そのタスクが破棄されるまで実行され続けます。