이터레이터(iterator)는 __next__()로 값을 하나씩 내주다가 끝나면 StopIteration을 던지는 객체다. 제너레이터(generator)는 yield를 쓴 함수로, 이터레이터를 클래스 없이 간단히 만드는 방법이다. 둘 다 값을 필요할 때 하나씩 만들어(지연 평가, lazy evaluation) 메모리를 아낀다.
for문이 하는 일
for x in items:
print(x)
# 실제로는
it = iter(items) # items.__iter__() → 이터레이터
while True:
try:
x = next(it) # it.__next__()
except StopIteration:
break
print(x)__iter__()를 가진 객체가 이터러블(iterable)(리스트, 딕셔너리,range, 파일), 그것이 돌려주는__next__()를 가진 객체가 이터레이터다- 이터레이터는 한 번 다 쓰면 끝이다. 리스트는 매번 새 이터레이터를 만들어 여러 번 돌 수 있다
제너레이터
def read_lines(path):
with open(path) as f:
for line in f:
if line.strip():
yield line.rstrip() # 여기서 멈췄다가 다음 next()에 이어서
for line in read_lines("big.log"): # 파일 전체를 메모리에 올리지 않음
...
squares = (n * n for n in range(10**9)) # 제너레이터 표현식: 아직 아무것도 계산 안 함yield는return과 달리 함수를 끝내지 않고 일시 정지한다. 지역 변수와 실행 위치가 그대로 남는다- 무한 수열, 큰 파일·스트리밍 데이터 처리, 파이프라인 구성에 좋다
yield from 다른이터러블로 하위 제너레이터에 위임한다- 비동기 코루틴(coroutine,
async def)도 이 "멈췄다 이어 가는 함수" 아이디어에서 나왔다(asyncio 태스크 예외가 조용히 사라지는 문제)
언제 쓰나
결과를 한 번만 순서대로 쓰고 버릴 거라면 리스트 대신 제너레이터가 낫다. 여러 번 쓰거나 길이·인덱스가 필요하면 리스트로 만든다. 반복을 변환 단계의 연결로 쓰는 습관은 반복문을 파이프라인으로 바꾸기과 같고, 자바스크립트의 이터레이터 프로토콜·제너레이터와도 거의 같은 모양이다. 지연 계산이 필요한 다른 예는 이벤트 루프의 비동기 처리에서 볼 수 있다.
출처: Python 용어집: iterator · generator · Python 튜토리얼: Iterators · Generators