제네레이터
Generator
yield로 값을 하나씩 생성하는 함수. 메모리 효율적인 이터레이션. Python, JavaScript에서 지원.
Generator
yield로 값을 하나씩 생성하는 함수. 메모리 효율적인 이터레이션. Python, JavaScript에서 지원.
제네레이터(Generator)는 yield 키워드를 사용하여 값을 하나씩 생성하는 특별한 함수입니다. 일반 함수처럼 한 번에 모든 결과를 반환하지 않고, 호출될 때마다 다음 값을 생성합니다. 이를 통해 메모리 효율적으로 시퀀스를 처리하고 무한 시퀀스도 표현할 수 있습니다.
제네레이터의 핵심은 실행 상태 유지입니다. yield에서 함수가 일시 정지되고, next()가 호출되면 그 지점부터 다시 실행됩니다. 함수의 지역 변수, 실행 위치 등 모든 상태가 보존됩니다. 이는 코루틴(coroutine)의 기반이 되는 메커니즘입니다.
Python에서 제네레이터는 두 가지 방식으로 만들 수 있습니다. yield를 포함한 함수를 정의하거나, 제네레이터 표현식(x*2 for x in range(10))을 사용합니다. 두 방식 모두 이터레이터 프로토콜을 자동으로 구현하므로 for 루프에서 바로 사용할 수 있습니다.
JavaScript에서는 function* 문법으로 제네레이터를 정의합니다. async/await는 실제로 제네레이터의 문법적 설탕으로 시작되었으며, Redux-Saga 같은 라이브러리가 제네레이터를 활용해 비동기 흐름 제어를 구현합니다. Rust에서는 Iterator 트레이트와 async/await가 유사한 개념을 제공합니다.
# Python 제네레이터 예제
# 기본 제네레이터 함수
def countdown(n):
while n > 0:
yield n
n -= 1
print("발사!")
for num in countdown(5):
print(num) # 5, 4, 3, 2, 1, "발사!"
# 무한 시퀀스 제네레이터
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
print([next(fib) for _ in range(10)]) # [0,1,1,2,3,5,8,13,21,34]
# 제네레이터 표현식
squares = (x**2 for x in range(10))
print(list(squares)) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 파일 처리 (메모리 효율적)
def read_large_file(file_path):
with open(file_path) as f:
for line in f:
yield line.strip()
# send()로 양방향 통신
def accumulator():
total = 0
while True:
value = yield total
if value is not None:
total += value
acc = accumulator()
next(acc) # 시작
print(acc.send(10)) # 10
print(acc.send(20)) # 30
데이터 엔지니어: "DB에서 100만 건을 가져와서 처리하는데 메모리가 부족합니다."
시니어 개발자: "한 번에 다 가져오지 말고 제네레이터로 청크 단위로 처리하면 어떨까요?"
데이터 엔지니어: "커서를 제네레이터로 감싸서 yield 하면 되겠네요."
시니어 개발자: "맞아요. 배치 사이즈를 1000 정도로 설정하고, 각 배치를 yield하면 메모리 사용량이 일정하게 유지됩니다."
면접관: "제네레이터와 리스트 컴프리헨션의 차이점은 무엇인가요?"
지원자: "리스트 컴프리헨션은 모든 요소를 즉시 생성해서 메모리에 저장하고, 제네레이터 표현식은 요청 시에만 값을 생성합니다. 대용량 데이터에서는 제네레이터가 메모리 효율이 훨씬 좋습니다."
면접관: "yield from은 어떤 용도인가요?"
지원자: "다른 제네레이터의 값을 위임할 때 사용합니다. 중첩된 반복문 대신 yield from sub_generator()로 간결하게 표현할 수 있습니다."
리뷰어: "이 함수에서 리스트에 append하고 마지막에 return하고 있는데, 제네레이터로 바꾸면 좋겠어요."
작성자: "return 대신 yield를 쓰면 되나요?"
리뷰어: "네, append 대신 yield하고, return 문은 제거하세요. 그러면 중간 리스트 없이 스트리밍 방식으로 처리됩니다."
작성자: "단점은 없나요?"
리뷰어: "한 번만 순회할 수 있다는 점이요. 여러 번 순회해야 하면 list()로 변환하거나 함수를 다시 호출해야 합니다."