← 파이썬 강의 목록으로
🚀
고급 (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()
재사용한 번만여러 번
인덱싱안 됨

자주 하는 실수

  1. 이터레이터를 두 번 순회 — 두 번째는 빈 결과. 다시 생성 필요
  2. __next__ 에서 StopIteration 누락 — 무한 루프
  3. 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.pyyield 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.pycombinations, 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.pysend 기반 코루틴
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]
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗