이 카드에서 핵심은 문법보다 반복 가능한 원본과 실제 진행 상태를 가진 반복기를 구분하는 것입니다.
iterable에서 iter()로 iterator를 만들고, iterator에서 next()로 값을 꺼내며, for는 이 과정을 자동으로 반복한다는 흐름이 잡히면 generator와 itertools도 같이 이해됩니다.
핵심 정리
items = [10, 20, 30]
it = iter(items)
print(next(it)) # 10
print(next(it)) # 20문법
기본형은 iterable, iter(), next(), for 흐름을 함께 보면 됩니다.
items = [1, 2, 3]
it = iter(items)
print(next(it))
for item in items:
print(item)iterable과 iterator
"반복 가능한 것"과 "이미 진행 중인 것"을 어떻게 나누는가
iterable은 iter(obj)를 호출해 iterator를 만들 수 있는 객체입니다.
items = [1, 2, 3]
text = "abc"
mapping = {"a": 1}즉 iterable은 "반복을 시작할 수 있는 원본"이라고 보면 됩니다. 대부분의 컬렉션은 여기에 속합니다.
iterator는 실제 진행 상태를 가진다
iterator는 __next__()를 가진 객체이고, 한 칸씩 전진하면서 값을 내놓습니다.
it = iter([1, 2, 3])
print(next(it)) # 1
print(next(it)) # 2이 카드에서 중요한 건 iterator가 단순한 "형태"가 아니라 현재 위치를 기억하는 상태 객체라는 점입니다.
순회 모델
for 문은 내부적으로 next()를 반복 호출한다
for item in items:는 문법이 간단할 뿐, 실제로는 iterator를 만들고 StopIteration이 나올 때까지 값을 꺼냅니다.
it = iter(["a", "b"])
while True:
try:
print(next(it))
except StopIteration:
break이 모델을 이해하면 generator와 파일 반복도 결국 같은 프로토콜 위에서 움직인다는 점이 보입니다.
iterator는 한 번 소비되면 비워진다
iterable은 다시 iter()를 호출해 새 iterator를 만들 수 있지만, iterator 자체는 현재 위치를 기억하므로 한 번 끝까지 읽으면 다시 처음으로 돌아가지 않습니다.
it = iter([1, 2])
print(list(it)) # [1, 2]
print(list(it)) # []이 점 때문에 디버깅 중 list(it)를 한 번 찍어 보는 행동이 실제 로직에 영향을 줄 수 있습니다.
재사용 기준
반복이 여러 번 필요하면 원본 iterable을 보관한다
두 번 이상 순회해야 하거나 길이를 다시 재야 하면 iterator보다 원본 iterable이나 리스트로 물질화된 결과를 유지하는 편이 안전합니다.
즉 iterator는 "흐름"이고, list는 "저장된 결과"에 더 가깝습니다.
주의할 점
iterator는 한 번 읽으면 소비됩니다. 디버깅 출력이나 중복 순회에서 값이 사라지는 가장 흔한 원인입니다.
# ❌ list()로 한 번 소비해 버림
it = iter([1, 2, 3])
print(list(it))
print(list(it)) # []
# ✅ 다시 순회해야 하면 원본 iterable을 보관
items = [1, 2, 3]
print(list(iter(items)))
print(list(iter(items)))참고 링크
2 sources