본문 바로가기
C.W.K.
Stream
Lesson 02 of 07 · published

제너레이터 — yield로 멈췄다가 이어가기

~22 min · generator, yield, lazy

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

상태를 직접 관리하지 않아도 되는 반복자

함수 본문에 yield가 있으면 호출 결과는 제너레이터야. next가 들어올 때 yield까지 실행하고 값을 내준 뒤 그 자리의 지역 변수와 실행 위치를 보존해. 다음 next에서 바로 다음 줄부터 이어가. 수동 반복자를 만들려던 사례의 약 90%는 이 모양으로 더 간단해져.

필요한 만큼만 만든다

제너레이터는 값을 모두 준비하지 않으므로 무한 수열이나 큰 파일도 다룰 수 있어. 소비자가 멈추면 뒤의 값은 계산조차 하지 않아. yield from은 다른 반복값의 원소를 차례로 넘겨 위임 코드를 줄여줘.

파일을 줄 단위로 흐르게 해

파일 객체 자체가 반복자이므로 한 줄씩 읽고 변환한 값을 yield하면 전체 파일을 메모리에 올리지 않는 처리 단계를 만들 수 있어.

send와 throw는 역사적 도구야

sendthrow는 제너레이터 안으로 값과 예외를 보낼 수 있어 초기 코루틴 형태에 쓰였어. 오늘의 비동기 코드는 비동기/await가 더 분명하므로, 기존 코드를 읽을 만큼만 이해해도 돼.

Code

yield하고 다음 호출까지 멈추기·python
def countdown(n):
    while n > 0:
        yield n
        n -= 1

for x in countdown(3):
    print(x)
# 3
# 2
# 1

# CountDown 클래스랑 같은 동작 — 8 줄 대신 3 줄
print(list(countdown(5)))    # [5, 4, 3, 2, 1]
print(sum(countdown(10)))    # 55
끝없는 수열을 필요할 때만 계산하기·python
def naturals():
    n = 1
    while True:
        yield n
        n += 1

# 끝까지 반복하지 않고 처음 5개만
import itertools
first_5 = list(itertools.islice(naturals(), 5))
print(first_5)               # [1, 2, 3, 4, 5]

# 답 찾자마자 멈춤
result = next(x for x in naturals() if x * x > 1000)
print(result)                # 32  (32*32 = 1024)
yield from으로 반복 위임하기·python
def first_half(items):
    yield from items[:len(items)//2]

def whole(items):
    yield from first_half(items)
    yield "middle marker"
    yield from items[len(items)//2:]

for x in whole([1, 2, 3, 4, 5, 6]):
    print(x)
# 1
# 2
# 3
# middle marker
# 4
# 5
# 6
큰 파일을 줄 단위로 처리하기·python
def lines_of(path):
    with open(path) as f:
        for line in f:
            yield line.rstrip("\n")

# 메모리 비용은 한 줄, 파일 크기 무관
# (실제 예제는 진짜 파일 가리킴)
# for line in lines_of("huge.log"):
#     if "ERROR" in line:
#         print(line)

# 필터 파이프라인 자연스럽게 합쳐짐
def errors_only(lines):
    for line in lines:
        if "ERROR" in line:
            yield line

# errors_only(lines_of("huge.log"))  # 여전히 streaming, list 절대 안 만들어짐
제너레이터에 값을 보내는 예전 코루틴 방식·python
def echo():
    while True:
        received = yield
        print("got:", received)

g = echo()
next(g)                      # prime — 첫 yield 까지 진행
g.send("hello")              # 'got: hello'
g.send("world")              # 'got: world'
g.close()                    # generator 종료 신호

External links

Exercise

take_until(predicate, iterable)을 만들어 predicate가 처음 참이 되는 원소 직전까지만 yield해. range(100)과 무한 자연수 제너레이터에서 확인하고, 마지막에는 yield from을 쓴 작은 chain(*generators)로 여러 제너레이터를 하나처럼 이어봐.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.