Python 이터레이터: 작동 원리와 직접 만들기
Python 이터레이터
Python 이터레이터 작동 방식, for 루프나 next() 함수로 순회하는 법, 이터레이터 프로토콜로 커스텀/무한 이터레이터 만드는 법 알아보기.
동영상: Python 이터레이터
Python 이터레이터
이터레이터는 Python 곳곳에 스며있습니다. for 루프, 리스트 컴프리헨션, 제너레이터, 많은 내장 자료구조의 기반이 되며 종종 보이지 않게 작동.
이터레이터는 단순히 데이터를 한 번에 한 요소씩 산출하는 객체. Python에서 __iter__()와 __next__() 메서드 구현한 객체는 이터레이터로 간주.
이터레이터 제공 가능한 객체를 이터러블(iterable)이라 함. 대다수 내장 컨테이너—리스트, 튜플, 문자열, 딕셔너리—가 이터러블. 내장 iter() 함수가 객체 __iter__() 호출해 이터레이터 획득.
이터레이터 순회하기
next() 함수로 이터레이터 수동 전진. 이터레이터 소진 시 StopIteration 발생.
# 리스트 정의
my_list = [4, 7, 0, 3]
# iter()로 이터레이터 획득
my_iter = iter(my_list)
# next()로 순회
# 출력: 4
print(next(my_iter))
# 출력: 7
print(next(my_iter))
# next(obj)는 obj.__next__()와 동일
# 출력: 0
print(my_iter.__next__())
# 출력: 3
print(my_iter.__next__())
# 아이템 없으면 에러
next(my_iter)
출력
4 7 0 3 Traceback (most recent call last): File "<string>", line 24, in <module> next(my_iter) StopIteration
더 자연스러운 순회는 for 루프로, 이터레이터 생명주기 자동 처리.
>>> for element in my_list:
... print(element)
...
4
7
0
3
for 루프가 이터레이터와 작동하는 원리
for 루프는 내부적으로 다음으로 변환:
for element in iterable:
# element로 무언가 수행
다음 로직과 동등:
# 이터러블에서 이터레이터 객체 생성
iter_obj = iter(iterable)
# 무한 루프
while True:
try:
# 다음 아이템 획득
element = next(iter_obj)
# element로 무언가 수행
except StopIteration:
# StopIteration 발생 시 루프 탈출
break
이게 for 루프가 본질적으로 이터레이터 프로토콜과 StopIteration 예외에 의존해 종료하는 무한 루프임을 보여줍니다.
커스텀 이터레이터 만들기
이터레이터 직접 만들기 간단: __iter__()로 이터레이터 인스턴스(보통 self) 반환, __next__()로 다음 값 산출하거나 끝나면 StopIteration 발생.
아래 예제는 사용자 지정 최대 지수까지 2의 거듭제곱 산출:
class PowTwo:
"""2의 거듭제곱 산출 이터레이터."""
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
# 객체 생성
numbers = PowTwo(3)
# 객체에서 이터러블 생성
i = iter(numbers)
# next로 다음 이터레이터 요소 획득
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))
출력
1 2 4 8 Traceback (most recent call last): File "/home/bsoyuj/Desktop/Untitled-1.py", line 32, in <module> print(next(i)) File "<string>", line 18, in __next__ raise StopIteration StopIteration
커스텀 이터레이터로 for 루프 사용도 동일하게 간단:
>>> for i in PowTwo(5):
... print(i)
...
1
2
4
8
16
32
Python 무한 이터레이터
이터레이터는 무한할 수 있음. 내장 iter()는 호출 가능 객체와 센티널 값 받음: 반환 값이 센티널과 같아질 때까지 함수 반복 호출. 예:
>>> int()
0
>>> inf = iter(int, 1)
>>> next(inf)
0
>>> next(inf)
0
int()가 항상 0 반환하므로 iter(int, 1)은 센티널 절대 못 봐 무한 이터레이터 생성.
홀수 모두 산출하는 자체 무한 이터레이터도 만들 수 있음:
class InfIter:
"""연속 홀수 반환 무한 이터레이터."""
def __iter__(self):
self.num = 1
return self
def __next__(self):
num = self.num
self.num += 2
return num
실행 예:
>>> a = iter(InfIter())
>>> next(a)
1
>>> next(a)
3
>>> next(a)
5
>>> next(a)
7
무한 이터레이터 다룰 땐 항상 종료 조건 제공해 무한 루프 방지.
이터레이터는 아이템 온디맨드 생성으로 메모리 효율적. 이론적으로 홀수 전체 같은 무한 시퀀스 전체 저장 없이 생성 가능.
이터레이터 더 간단히 만드는 법: yield 사용 Python 제너레이터 탐색.