yield
키워드 설명
yield
키워드는 Python에서 제너레이터(Generator) 함수를 정의할 때 사용되는 특별한 키워드입니다. 제너레이터는 이터레이터(Iterator)의 한 종류로, 값을 한 번에 모두 메모리에 저장하는 대신, 필요할 때마다 하나씩 생성하여 반환합니다.
1. 제너레이터 (Generator)
- 이터레이터(Iterator):
__iter__()
와__next__()
메서드를 구현하여 값을 순차적으로 꺼낼 수 있는 객체입니다. 리스트, 튜플, 문자열 등이 이터레이터의 예입니다. - 제너레이터(Generator):
yield
키워드를 사용하여 이터레이터를 간단하게 생성하는 방법입니다. 함수 내에서yield
를 만나면, 해당 값을 반환하고 함수의 실행을 일시 중지합니다. 다음에 다시 호출되면, 중지되었던 지점부터 다시 실행을 재개합니다.
2. yield
키워드의 동작
- 값 반환:
yield
문은 함수 실행 중 값을 반환합니다.return
문과 유사하지만,return
은 함수를 종료시키는 반면,yield
는 함수의 상태를 저장하고 일시 중지합니다. - 실행 중지 및 재개:
yield
를 만나면 함수는 해당 값을 반환하고 실행을 일시 중지합니다. 제너레이터가 다시 호출(next()
함수 사용)되면, 중지되었던yield
문 다음부터 다시 실행을 시작합니다.
3. 예제
def simple_generator():
yield 1
yield 2
yield 3
# 제너레이터 객체 생성
gen = simple_generator()
# 제너레이터 사용
print(next(gen)) # 출력: 1
print(next(gen)) # 출력: 2
print(next(gen)) # 출력: 3
# print(next(gen)) # StopIteration 예외 발생
simple_generator
함수는yield
를 사용하여 1, 2, 3을 순차적으로 반환하는 제너레이터입니다.next(gen)
을 호출할 때마다yield
문을 만나 값을 반환하고, 함수는 일시 중지됩니다. 다음next(gen)
호출 시 중지되었던 지점부터 다시 실행됩니다.- 더 이상 반환할
yield
가 없으면StopIteration
예외가 발생합니다.
4. 제너레이터의 장점
- 메모리 효율성: 값을 미리 생성하여 메모리에 저장하지 않고, 필요할 때마다 하나씩 생성하므로 메모리 사용량이 적습니다. 특히, 매우 큰 데이터 시퀀스를 다룰 때 유용합니다.
- 지연 평가 (Lazy Evaluation): 값이 실제로 필요할 때까지 계산을 미루므로, 불필요한 계산을 줄일 수 있습니다.
- 코드 간결성: 이터레이터를 직접 구현하는 것보다 코드가 간결하고 가독성이 좋습니다.
5. 제너레이터 표현식 (Generator Expression)
리스트 컴프리헨션(List Comprehension)과 유사한 구문으로 제너레이터를 간단하게 생성할 수 있습니다.
# 리스트 컴프리헨션
squares_list = [x**2 for x in range(10)]
# 제너레이터 표현식
squares_gen = (x**2 for x in range(10))
print(squares_list) # 리스트 출력
print(squares_gen) # 제너레이터 객체 출력
# 제너레이터 사용
for square in squares_gen:
print(square) # 값들이 하나씩 출력
- 리스트 컴프리헨션은
[]
를 사용하고, 제너레이터 표현식은()
를 사용합니다. - 제너레이터 표현식은 리스트 컴프리헨션과 달리, 즉시 모든 값을 계산하지 않고 필요할 때 하나씩 계산합니다.
6. yield from
(Python 3.3 이상)
yield from
은 다른 제너레이터나 이터러블 객체로부터 값을 가져와 yield
할 때 유용합니다.
def chain_generators(gen1, gen2):
yield from gen1
yield from gen2
# 두 개의 제너레이터
gen1 = (i for i in range(3))
gen2 = (i for i in range(3, 6))
# 제너레이터 연결
chained_gen = chain_generators(gen1, gen2)
for value in chained_gen:
print(value) # 0, 1, 2, 3, 4, 5 출력
yield from
을 사용하면 중첩된 루프 없이도 여러 제너레이터/이터러블의 값을 순차적으로 가져올 수 있어 코드가 더 간결해집니다.
yield
키워드: 코드 실행 중단 및 값 반환
앞선 설명에 yield
를 만났을 때 코드 실행이 중단되고 값을 반환하는 개념을 좀 더 명확하게 추가하여 설명하겠습니다.
yield
키워드의 핵심 동작: 중단과 반환
yield
키워드는 제너레이터 함수 내에서 다음과 같은 두 가지 핵심 역할을 수행합니다.
값 반환 (Return Value):
yield
문은return
문처럼 함수 실행 중에 값을 반환합니다.return
은 함수를 완전히 종료하고 값을 반환하는 반면,yield
는 값을 반환하면서도 함수의 상태(로컬 변수, 실행 위치 등)를 그대로 유지합니다.
실행 중단 (Suspend Execution):
yield
문을 만나면, 제너레이터 함수는 실행을 일시 중지합니다.- 함수의 실행은
yield
문 바로 다음 위치에서 멈추고, 제너레이터의 제어권은 호출자(caller)에게 넘어갑니다. - 이때, 함수의 로컬 변수와 현재 실행 위치 등의 상태는 메모리에 그대로 보존됩니다. 마치 일시 정지 버튼을 누른 것과 같습니다.
재개 (Resume)
- 제너레이터가 다시 호출(
next()
함수 또는for
루프 등)되면, 중단되었던yield
문 다음 코드부터 실행을 재개합니다. - 마치 일시 정지했던 비디오를 다시 재생하는 것과 같습니다.
- 함수는 이전 상태를 기억하고 있기 때문에, 마치 중단되지 않았던 것처럼 자연스럽게 다음 코드를 실행합니다.
비유를 통한 이해
yield
의 동작을 요리 과정에 비유해 보겠습니다.
def make_pizza():
print("피자 도우 준비")
yield "도우" # 도우 준비 완료, 호출자에게 도우를 주고 잠시 대기
print("토핑 추가")
yield "토핑" # 토핑 추가 완료, 호출자에게 토핑을 주고 잠시 대기
print("피자 굽기")
yield "완성된 피자" # 피자 완성, 호출자에게 피자를 줌
pizza_gen = make_pizza()
step1 = next(pizza_gen) # "피자 도우 준비" 출력, "도우" 반환, 실행 중단
print(f"지금까지: {step1}")
step2 = next(pizza_gen) # (중단된 위치에서 다시 시작) "토핑 추가" 출력, "토핑" 반환, 실행 중단
print(f"지금까지: {step1}, {step2}")
step3 = next(pizza_gen) # (중단된 위치에서 다시 시작) "피자 굽기" 출력, "완성된 피자" 반환, 실행 중단
print(f"지금까지: {step1}, {step2}, {step3}")
make_pizza()
함수는 피자를 만드는 과정을 나타내는 제너레이터입니다.yield
를 만날 때마다, 현재 단계의 결과물(도우, 토핑, 완성된 피자)을 반환하고 요리사의 작업은 잠시 멈춥니다.next()
를 통해 다시make_pizza()
를 호출하면, 멈췄던 부분부터 이어서 요리를 진행합니다.
예제: 무한 수열
def infinite_sequence():
num = 0
while True:
yield num
num += 1
gen = infinite_sequence()
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 2
# ... 무한히 많은 값을 생성 가능
infinite_sequence
함수는yield
를 통해 무한히 증가하는 숫자를 생성합니다.next(gen)
을 호출할 때마다 새로운 숫자를 반환하고, 함수는 다음 호출을 위해 대기 상태가 됩니다.- 일반적인 함수였다면 무한 루프에 빠지겠지만, 제너레이터는
yield
덕분에 필요할 때마다 값을 생성하고 중단하는 것을 반복할 수 있습니다.
이처럼 yield
는 함수의 실행을 일시 중지하고 값을 반환하는 독특한 메커니즘을 통해 제너레이터를 구현하는 핵심적인 역할을 합니다. 제너레이터는 메모리 효율성과 지연 평가 등의 장점을 제공하여 Python 프로그래밍에서 유용하게 활용됩니다.
'프로그래밍 > Python' 카테고리의 다른 글
파이썬 - Coroutine과 await (0) | 2025.03.20 |
---|---|
파이썬 - Generator & Iterator (0) | 2025.03.19 |
파이썬 - pass 키워드 (0) | 2025.03.19 |
파이썬 - 클래스 개념 (0) | 2025.03.19 |
Python yield (0) | 2024.08.06 |