프로그래밍/Python

파이썬 - yield 키워드

Terry Cho 2025. 3. 19. 17:02

yield 키워드 설명

yield 키워드는 Python에서 제너레이터(Generator) 함수를 정의할 때 사용되는 특별한 키워드입니다. 제너레이터는 이터레이터(Iterator)의 한 종류로, 값을 한 번에 모두 메모리에 저장하는 대신, 필요할 때마다 하나씩 생성하여 반환합니다.

1. 제너레이터 (Generator)

  • 이터레이터(Iterator): __iter__()__next__() 메서드를 구현하여 값을 순차적으로 꺼낼 수 있는 객체입니다. 리스트, 튜플, 문자열 등이 이터레이터의 예입니다.
  • 제너레이터(Generator): yield 키워드를 사용하여 이터레이터를 간단하게 생성하는 방법입니다. 함수 내에서 yield를 만나면, 해당 값을 반환하고 함수의 실행을 일시 중지합니다. 다음에 다시 호출되면, 중지되었던 지점부터 다시 실행을 재개합니다.

2. yield 키워드의 동작

  1. 값 반환: yield 문은 함수 실행 중 값을 반환합니다. return 문과 유사하지만, return은 함수를 종료시키는 반면, yield는 함수의 상태를 저장하고 일시 중지합니다.
  2. 실행 중지 및 재개: 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 키워드는 제너레이터 함수 내에서 다음과 같은 두 가지 핵심 역할을 수행합니다.

  1. 값 반환 (Return Value):

    • yield 문은 return 문처럼 함수 실행 중에 값을 반환합니다.
    • return은 함수를 완전히 종료하고 값을 반환하는 반면, yield는 값을 반환하면서도 함수의 상태(로컬 변수, 실행 위치 등)를 그대로 유지합니다.
  2. 실행 중단 (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}")
  1. make_pizza() 함수는 피자를 만드는 과정을 나타내는 제너레이터입니다.
  2. yield를 만날 때마다, 현재 단계의 결과물(도우, 토핑, 완성된 피자)을 반환하고 요리사의 작업은 잠시 멈춥니다.
  3. 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