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

작은 제너레이터를 이어 처리 흐름 만들기

~20 min · pipeline, stream, composition

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

한 단계는 한 가지 변환만 맡아

읽기, 정리, 파싱, 필터링, 변환을 작은 제너레이터로 나누면 각 단계가 반복값을 받아 반복값을 내줘. 그러면 단계를 조합해도 전체 입력을 중간 리스트에 담지 않아.

지연 실행은 메모리 절약 이상의 계약이야

큰 원본을 조금씩 다룰 수 있고, 소비자가 멈추면 앞 단계도 더 일하지 않아. 대신 제너레이터를 만들 때 본문이 실행되지 않으므로 예외와 부수 효과도 실제 소비 시점까지 미뤄져.

구체화 지점을 의식적으로 정해

중간에 list를 만들면 그곳에서 전체 흐름이 한꺼번에 계산돼. 정렬·다중 소비·길이 확인처럼 정말 필요한 경계에서만 구체화하고, 그렇지 않으면 마지막 소비자까지 흐르게 둬.

주의: 지연된 I/O는 파일 수명, 취소, 제한시간도 소비 시점까지 끌고 와. 열린 자원의 경계를 파이프라인보다 짧게 만들지 마.

Code

다섯 단계로 잇는 제너레이터 흐름·python
def lines_of(text):
    yield from text.splitlines()

def strip_each(lines):
    for line in lines:
        yield line.strip()

def drop_empty(lines):
    for line in lines:
        if line:
            yield line

def first_word(lines):
    for line in lines:
        yield line.split(" ", 1)[0]

def long_only(words, min_len=4):
    for w in words:
        if len(w) >= min_len:
            yield w

source = """hello world\n\n  foo bar\nhi there\nlongword something"""

result = list(long_only(first_word(drop_empty(strip_each(lines_of(source))))))
print(result)              # ['hello', 'longword']
원본이 클수록 커지는 지연 처리의 이점·python
import itertools as it

def naturals():
    n = 1
    while True:
        yield n
        n += 1

def squares(nums):
    for n in nums:
        yield n * n

def under_limit(nums, limit):
    for n in nums:
        if n >= limit:
            return
        yield n

# 파이프라인: naturals -> squares -> 10000 미만
result = list(under_limit(squares(naturals()), 10000))
print(result)
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, ...]
# 99*99 = 9801 에서 멈춤 (다음은 10000)
오류도 소비할 때 발생한다·python
def risky():
    print("about to fail")
    raise ValueError("boom")
    yield 1                  # 도달하지 않지만 함수는 여전히 generator

g = risky()                  # 출력도 에러도 없음 — 본문 아직 안 돌아
print("got generator")

try:
    next(g)
except ValueError as e:
    print("raised at iteration time:", e)
# about to fail
# raised at iteration time: boom
중간 list가 지연 처리를 끊는 지점·python
import itertools as it

def big_source():
    for i in range(10**6):
        yield i

# 안 좋음 — 슬라이싱 전에 백만 원소 list 통째 materialize
result_bad = list(it.islice(list(big_source()), 5))

# 좋음 — 끝까지 lazy
result_good = list(it.islice(big_source(), 5))

print(result_bad == result_good)    # True — 같은 답
# 근데 result_bad 가 가는 길에 백만 원소 list 할당

External links

Exercise

주어진 CSV 모양 문자열에 네 단계 제너레이터를 이어. 빈 줄을 없애고, 쉼표가 있는 줄만 남기고, (name, int(age))로 바꾸되 변환 실패는 건너뛰고, age가 18 이상인 튜플만 내줘. 마지막에만 리스트로 모아 실패한 줄이 제외됐는지 확인해.

Progress

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

댓글 0

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

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