Python yield 완벽 가이드: 효율적인 데이터 처리와 제너레이터 사용법

Python을 배우다 보면 yield라는 키워드를 만날 때가 있습니다. yield함수의 실행을 잠시 멈추고, 데이터를 하나씩 반환하는 특별한 역할을 하는 키워드입니다. 특히 yield를 사용하면 큰 데이터나 반복 작업을 더 효율적으로 처리할 수 있어요. 이번 글에서는 yield의 개념을 이해하고, yield를 활용해 제너레이터를 만드는 방법과 함께 실제 코드에서 유용하게 활용할 수 있는 예제를 소개합니다.


yield란 무엇일까? - 제너레이터와의 관계

yield는 Python의 함수에서 데이터를 반환하고 함수의 상태를 유지하는 키워드입니다. yield를 만나면 함수는 그 시점에서 잠시 멈추고, 값을 반환한 후 필요할 때 다시 실행을 시작합니다. 이 yield제너레이터(generator)를 만드는 핵심입니다.

제너레이터란?

제너레이터는 yield를 사용해 만든 반복 가능한 객체입니다. 일반적인 함수는 값을 한 번에 모두 반환하는 반면, 제너레이터는 필요한 시점에 값을 하나씩 반환하여 메모리 사용을 줄입니다. 이 덕분에 데이터가 많아도 메모리에 부담 없이 다룰 수 있는 것이 큰 장점입니다.


기본 예제: yield를 사용해 제너레이터 만들기

yield를 사용해 기본적인 제너레이터 함수를 만들어보겠습니다. 이 함수는 1부터 n까지의 숫자를 차례로 반환합니다.

def count_up_to(n):
    count = 1
    while count <= n:
        yield count
        count += 1

# 제너레이터 호출
counter = count_up_to(5)
print(next(counter))  # 1
print(next(counter))  # 2
print(next(counter))  # 3
print(next(counter))  # 4
print(next(counter))  # 5

결과

1
2
3
4
5

설명

  • yield countcount 값을 반환하고 함수의 상태를 유지합니다.
  • next(counter)를 호출할 때마다 함수가 중단된 곳부터 다시 시작하며, 다음 값을 반환합니다.
  • count_up_to(5) 제너레이터는 1부터 5까지의 숫자를 순서대로 반환합니다.

yieldreturn의 차이점 이해하기

함수에서 yield 대신 return을 사용하면 제너레이터가 아니라 일반 함수가 됩니다. 일반 함수는 값을 모두 계산해 한 번에 반환하는 반면, yield는 필요한 시점에 하나씩 값을 전달합니다.

def normal_function():
    return 1
    return 2  # 이 부분은 실행되지 않습니다

def generator_function():
    yield 1
    yield 2

# 결과 확인
print(normal_function())  # 1
for value in generator_function():
    print(value)  # 1, 2

결과

1
1
2

설명

  • normal_function()은 1을 반환하고 함수가 끝납니다. 이후 코드는 실행되지 않습니다.
  • generator_function()yield를 통해 값을 순서대로 반환합니다. 제너레이터는 for문을 통해 각 값을 하나씩 출력할 수 있습니다.

yield의 실제 활용 예제: 큰 데이터 처리하기

큰 데이터를 다룰 때 yield메모리 사용을 최소화하는 데 매우 유용합니다. 예를 들어, 큰 파일을 한 줄씩 읽어들이는 제너레이터를 만들어보겠습니다.

def read_large_file(file_path):
    with open(file_path, "r") as file:
        for line in file:
            yield line.strip()  # 한 줄씩 반환

# 파일 읽기
for line in read_large_file("large_text_file.txt"):
    print(line)

설명

  • read_large_file 제너레이터는 파일을 한 줄씩 읽고, 각 줄을 yield를 통해 반환합니다.
  • 큰 파일을 한꺼번에 메모리에 올리지 않기 때문에 파일 크기와 상관없이 효율적으로 읽을 수 있습니다.

yield를 이용한 무한 시퀀스 생성

일정한 패턴의 무한 시퀀스를 생성할 때도 yield는 유용합니다. 아래 예제는 피보나치 수열을 생성하는 제너레이터입니다.

def fibonacci_sequence():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 피보나치 수열 출력
fibo_gen = fibonacci_sequence()
for _ in range(10):
    print(next(fibo_gen))

결과

0
1
1
2
3
5
8
13
21
34

설명

  • fibonacci_sequence()는 무한히 피보나치 수열을 생성합니다.
  • next(fibo_gen)를 호출할 때마다 yield로 인해 다음 피보나치 숫자를 반환합니다.
  • 무한 시퀀스를 생성할 때 yield는 효율적이고 간결하게 코드를 작성할 수 있는 방법입니다.

yield from으로 중첩된 제너레이터 처리하기

Python에서는 yield from 문법을 사용해 중첩된 제너레이터에서 값을 가져올 수 있습니다. 여러 제너레이터에서 데이터를 합쳐서 반환할 때 유용합니다.

def generator1():
    yield "A"
    yield "B"

def generator2():
    yield from generator1()
    yield "C"
    yield "D"

# 실행 예시
for value in generator2():
    print(value)

결과

A
B
C
D

설명

  • generator2yield from generator1()을 통해 generator1의 값을 모두 반환한 후 자신의 값을 추가로 반환합니다.
  • yield from을 사용하면 중첩된 제너레이터에서 값을 한꺼번에 가져올 수 있어 코드가 간결해집니다.

실전 예제: 데이터 파이프라인에서 yield 활용하기

데이터를 여러 단계로 처리해야 할 때 yield는 중간 결과를 순차적으로 전달해주므로 매우 유용합니다. 예를 들어, 데이터를 필터링하고 처리하는 파이프라인을 구성할 수 있습니다.

def data_generator(data):
    for item in data:
        yield item

def filter_data(data_gen, threshold):
    for item in data_gen:
        if item > threshold:
            yield item

def process_data(filtered_data):
    for item in filtered_data:
        yield item * 2

# 데이터 생성 및 처리
data = [1, 5, 10, 15, 20]
data_gen = data_generator(data)
filtered_data = filter_data(data_gen, 10)
processed_data = process_data(filtered_data)

for item in processed_data:
    print(item)

결과

20
30
40

설명

  • data_generator는 데이터를 생성하고, filter_data는 조건에 맞는 데이터만 필터링하며, process_data는 필터링된 데이터에 처리를 적용합니다.
  • 각 단계에서 yield를 사용하여 데이터를 필요한 만큼만 처리하고 전달하므로 메모리를 절약하면서 효율적으로 데이터를 다룰 수 있습니다.

요약: yield의 장점과 활용법

Python에서 yield메모리 효율적인 데이터 처리를 가능하게 해주며, 제너레이터를 통해 값이 필요할 때마다 순차적으로 데이터를 생성할 수 있습니다. 이를 통해 코드가 간결해지고, 큰 데이터를 다루거나 무한 반복 작업을 수행할 때 매우 유용합니다.

yield는 특히 다음과 같은 상황에서 유용하게 활용됩니다:

  1. 큰 데이터를 처리할 때: 메모리 절약을 통해 효율적으로 데이터를 읽을 수 있습니다.
  2. 무한 반복 생성이 필요한 경우: yield를 사용해 무한 시퀀스를 간단히 구현할 수 있습니다.
  3. 중첩된 데이터를 다룰 때: yield from을 사용해 중첩된 제너레이터를 간단히 처리할 수 있습니다.

+ Recent posts