8.11. 비동기 이터레이터¶
비동기 이터레이터(async iterator)는 이터레이터의 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는 정상적으로 종료됩니다. 결코 끝나지 않는 소스 – 연속적인 센서 스트림, 장기간 실행되는 구독 – 의 경우, 루프는 자신이 속한 코루틴이 취소되거나 해당 태스크가 해체될 때까지 실행됩니다.