🚀
고급 (Advanced)
프로토콜 · yield from · itertools · 코루틴
3주차 — 이터레이터·제너레이터 심화
for 루프 뒤에 숨은 __iter__/__next__ 프로토콜, yield from 위임, itertools 의 강력한 함수들, 그리고 코루틴 기초까지 다룹니다.
iteratorgeneratoritertoolscoroutine
소요 시간
⏱ 2시간
난이도
📊 고급
선수 조건
🎯 고급 2주차 + 중급 2주차
결과물
직접 구현한 이터레이터와 itertools 활용
이 강의에서 배우는 것
- 1이터레이터 프로토콜(__iter__, __next__)을 직접 구현한다
- 2yield from 으로 제너레이터를 위임한다
- 3itertools 의 함수들을 활용한다
- 4send 기반 코루틴 기초를 안다
1. 이터레이터 프로토콜
for ... in x 는 사실 iter(x) → next(it) 반복입니다.
python
class Range:
def __init__(self, start, stop):
self.cur = start
self.stop = stop
def __iter__(self):
return self
def __next__(self):
if self.cur >= self.stop:
raise StopIteration
self.cur += 1
return self.cur - 1
print(list(Range(1, 5))) # [1, 2, 3, 4]2. yield from
다른 이터러블을 그대로 yield. 평탄화에 유용합니다.
python
def chain(*iters):
for it in iters:
yield from it
print(list(chain([1, 2], (3, 4), "ab"))) # [1, 2, 3, 4, 'a', 'b']3. itertools
표준 라이브러리의 보석.
python
import itertools as it
# 무한 시퀀스
counter = it.count(1, 2) # 1, 3, 5, 7, ...
print(list(it.islice(counter, 5))) # [1, 3, 5, 7, 9]
# 조합/순열
print(list(it.combinations([1, 2, 3, 4], 2)))
print(list(it.permutations([1, 2, 3], 2)))
# 그룹
data = [("a", 1), ("a", 2), ("b", 3), ("b", 4)]
for key, group in it.groupby(data, key=lambda x: x[0]):
print(key, list(group))
# 사전 곱
print(list(it.product([1, 2], "ab")))
# 누적
print(list(it.accumulate([1, 2, 3, 4]))) # [1, 3, 6, 10]4. 코루틴 기초
yield 가 양방향 통신 — send(값) 으로 값 주입.
python
def echo():
while True:
x = yield
print(f"받음: {x}")
co = echo()
next(co) # 첫 yield까지 진행 (필수)
co.send("hello")
co.send("world")Python 3.5+ 부터는 async/await 가 더 일반적 — 다음 주차에서.
5. 제너레이터 vs 리스트
| 제너레이터 | 리스트 | |
|---|---|---|
| 메모리 | 항목 1개씩 | 전체 |
| 길이 | 모름 | len() |
| 재사용 | 한 번만 | 여러 번 |
| 인덱싱 | 안 됨 | 됨 |
자주 하는 실수
- 이터레이터를 두 번 순회 — 두 번째는 빈 결과. 다시 생성 필요
- __next__ 에서 StopIteration 누락 — 무한 루프
- groupby 전 정렬 안 함 — groupby는 연속된 같은 키만 묶음
FAQ
Q1. 이터레이터와 제너레이터의 차이? — 제너레이터는 이터레이터의 한 종류. yield 로 만든 것.
Q2. itertools 보다 more-itertools 가 좋다는데? — 표준은 아니지만 강력. 실무에서 자주 사용.
💻 예제 (examples)
실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.
01_iterator.py— 직접 이터레이터 구현
CODE
class Range:
def __init__(self, start, stop):
self.cur = start
self.stop = stop
def __iter__(self):
return self
def __next__(self):
if self.cur >= self.stop:
raise StopIteration
self.cur += 1
return self.cur - 1
print(list(Range(1, 5)))▶ 실행 결과
[1, 2, 3, 4]02_yield_from.py— yield from
CODE
def chain(*iters):
for it in iters:
yield from it
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
print(list(chain([1, 2], (3, 4), "ab")))
print(list(flatten([1, [2, [3, [4, 5]]], 6])))▶ 실행 결과
[1, 2, 3, 4, 'a', 'b']
[1, 2, 3, 4, 5, 6]03_itertools.py— combinations, groupby, accumulate
CODE
import itertools as it
print(list(it.islice(it.count(1, 2), 5)))
print(list(it.combinations([1, 2, 3, 4], 2)))
data = sorted([("b", 3), ("a", 1), ("a", 2)], key=lambda x: x[0])
for key, group in it.groupby(data, key=lambda x: x[0]):
print(key, list(group))
print(list(it.accumulate([1, 2, 3, 4])))▶ 실행 결과
[1, 3, 5, 7, 9]
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
a [('a', 1), ('a', 2)]
b [('b', 3)]
[1, 3, 6, 10]04_coroutine.py— send 기반 코루틴
CODE
def echo():
while True:
x = yield
print(f"받음: {x}")
co = echo()
next(co)
co.send("hello")
co.send("world")
co.close()▶ 실행 결과
받음: hello
받음: world📝 과제 (exercises)
직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.
과제 1
chunked 제너레이터
목표: 리스트를 n개씩 잘라서 yield 하는 제너레이터를 만든다.
요구사항
- chunked(iterable, n) -> generator
- 마지막 청크는 n보다 작아도 됨
💡 힌트
슬라이싱 또는 itertools.islice 사용
입출력 예시
[1, 2, 3]
[4, 5, 6]
[7]채점
- · 제너레이터 함수
- · 정확한 청크 분할
▶정답 코드 펼치기 / 접기
SOLUTION
def chunked(iterable, n):
buf = []
for x in iterable:
buf.append(x)
if len(buf) == n:
yield buf
buf = []
if buf:
yield buf
for c in chunked([1, 2, 3, 4, 5, 6, 7], 3):
print(c)▶ 실행 결과
[1, 2, 3]
[4, 5, 6]
[7]과제 2
groupby 로 그룹화
목표: (date, message) 튜플 리스트를 날짜별로 그룹화한다.
요구사항
- itertools.groupby 사용
- 그룹 전 정렬 필수
💡 힌트
sorted(data, key=lambda x: x[0])
입출력 예시
2026-05-09: 2건
2026-05-10: 1건채점
- · groupby 사용
- · 정확한 카운트
▶정답 코드 펼치기 / 접기
SOLUTION
import itertools as it
logs = [
("2026-05-10", "INFO"),
("2026-05-09", "ERROR"),
("2026-05-09", "WARN"),
]
logs.sort(key=lambda x: x[0])
for date, group in it.groupby(logs, key=lambda x: x[0]):
print(f"{date}: {len(list(group))}건")▶ 실행 결과
2026-05-09: 2건
2026-05-10: 1건과제 3
무한 피보나치 + islice
목표: 무한 피보나치 제너레이터에서 처음 N개를 꺼낸다.
요구사항
- fibs() 가 무한 제너레이터
- itertools.islice 로 N개 추출
💡 힌트
a, b = b, a + b 패턴
입출력 예시
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]채점
- · 무한 yield
- · islice 사용
- · 정확한 N개
▶정답 코드 펼치기 / 접기
SOLUTION
import itertools as it
def fibs():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
print(list(it.islice(fibs(), 10)))▶ 실행 결과
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]