Python의 **제너레이터(generator)**는 대용량 데이터를 처리할 때 유용하지만,

대부분의 개발자들은 yield를 단순히 값을 반환하는 용도로만 사용한다.

 

하지만, yield에는 숨겨진 강력한 기능이 있다.

👉 send()를 사용하면 제너레이터에 데이터를 주고받을 수 있으며, 이를 활용하면 상태를 유지하는 코루틴을 만들 수 있다.

👉 이 기능을 제대로 활용하면, 일반적인 함수보다 훨씬 더 강력한 상태 관리가 가능하다.

 

이번 글에서는 제너레이터와 send()를 활용한 고급 프로그래밍 기법을 소개한다. 🚀

1. 대부분의 개발자가 아는 yield의 기본적인 사용법

 

일반적으로 yield는 값을 반환하고, 다음 호출에서 다시 실행할 수 있도록 하는 기능을 한다.

def simple_generator():
    yield 1
    yield 2
    yield 3

gen = simple_generator()
print(next(gen))  # 1
print(next(gen))  # 2
print(next(gen))  # 3

기본적인 yield의 동작: next()를 호출할 때마다 yield 다음의 값을 반환한다.

하지만 여기까지가 대부분의 개발자가 알고 있는 yield의 기능이다.

2. send()를 사용하여 제너레이터와 데이터를 주고받기

 

yield는 단순히 값을 반환하는 기능뿐만 아니라, 외부에서 데이터를 받아 제너레이터 내부에서 활용할 수도 있다.

이를 가능하게 하는 것이 바로 send() 함수다.

def echo():
    while True:
        received = yield  # yield가 값을 반환받는 역할도 수행
        print(f"Received: {received}")

gen = echo()
next(gen)  # 제너레이터 실행 준비 (첫 번째 `yield`까지 실행됨)

gen.send("Hello")  # Received: Hello
gen.send("Python")  # Received: Python

📌 send()가 하는 역할

1️⃣ yield는 값을 반환하는 것뿐만 아니라, 외부에서 값을 받을 수도 있다.

2️⃣ send(value)를 호출하면, yield가 해당 값을 받아서 처리할 수 있다.

3️⃣ 일반적인 next()와 달리, 제너레이터 내부의 변수 값을 동적으로 변경할 수 있다.

 

즉, yield를 사용하면 단순한 이터레이터(iterator) 역할뿐만 아니라, 상태를 유지하는 코루틴(coroutine) 역할도 가능하다!

3. send()를 활용한 제너레이터 기반의 상태 머신 구현

 

이제 send()를 사용하여 상태를 유지하는 제너레이터 기반의 상태 머신을 만들어 보자.

def state_machine():
    state = "INIT"
    while True:
        value = yield state  # 현재 상태를 반환하고, 새로운 값을 받음
        if value == "run":
            state = "RUNNING"
        elif value == "stop":
            state = "STOPPED"
        elif value == "reset":
            state = "INIT"

gen = state_machine()
print(next(gen))  # INIT (초기 상태)

print(gen.send("run"))   # RUNNING
print(gen.send("stop"))  # STOPPED
print(gen.send("reset")) # INIT

📌 설명

1️⃣ yield를 통해 현재 상태를 반환하고, send()를 통해 새로운 상태 값을 받을 수 있다.

2️⃣ send("run")을 호출하면 "RUNNING" 상태로 변경되고, send("stop")을 호출하면 "STOPPED" 상태로 변경된다.

3️⃣ 이처럼 yieldsend()를 조합하면 상태를 유지하는 프로그램을 쉽게 만들 수 있다!

 

일반적인 함수는 실행이 끝나면 상태를 유지할 수 없지만, 제너레이터는 상태를 유지하면서 실행을 계속할 수 있다.

4. send()와 yield를 활용한 데이터 스트리밍 시스템

 

제너레이터와 send()를 활용하면 실시간 데이터 스트리밍 시스템을 구현할 수도 있다.

예를 들어, 실시간으로 데이터를 분석하는 시스템을 만들어보자.

def data_stream_processor():
    total = 0
    count = 0
    avg = None

    while True:
        value = yield avg  # 현재 평균값 반환
        if value is not None:
            total += value
            count += 1
            avg = total / count  # 새로운 평균값 계산

gen = data_stream_processor()
print(next(gen))  # None (초기값)

print(gen.send(10))  # 10.0
print(gen.send(20))  # 15.0
print(gen.send(30))  # 20.0
print(gen.send(40))  # 25.0

📌 설명

1️⃣ yield는 현재 평균값을 반환하고, 새로운 값을 받아서 업데이트한다.

2️⃣ send(10), send(20)을 호출할 때마다 평균값이 업데이트된다.

3️⃣ 실시간으로 데이터를 처리하면서 평균을 구하는 시스템이 완성됨!

 

send()를 활용하면, 데이터를 지속적으로 받아서 처리하는 실시간 분석 시스템을 간단하게 만들 수 있다!

5. send()와 throw()를 활용한 예외 처리

 

send()를 활용하면 제너레이터의 흐름을 조작할 수 있는데,

이와 함께 throw()를 사용하면 제너레이터 내부에서 특정 예외를 발생시키는 기능도 추가할 수 있다.

def controlled_generator():
    try:
        while True:
            value = yield
            print(f"Processing: {value}")
    except Exception as e:
        print(f"Generator stopped due to error: {e}")

gen = controlled_generator()
next(gen)  # 제너레이터 실행 준비

gen.send(10)  # Processing: 10
gen.send(20)  # Processing: 20
gen.throw(ValueError, "Something went wrong!")  # Generator stopped due to error: Something went wrong!

📌 설명

1️⃣ throw(ExceptionType, message)를 호출하면 제너레이터 내부에서 예외가 발생한다.

2️⃣ 예외가 발생하면 except 블록이 실행되며, 이후 제너레이터가 종료된다.

3️⃣ 이 방법을 사용하면 제너레이터 내부에서 특정 조건이 발생했을 때, 예외를 던지고 종료할 수 있다.

 

throw()를 활용하면, 데이터 처리 중 특정 예외가 발생했을 때 빠르게 종료하는 로직을 만들 수 있다.

6. 정리: send()를 활용하면 얻을 수 있는 것

기능기존 방식send() 활용

단순한 값 반환 yield만 사용 send()로 동적 값 변경 가능
상태 유지 불가능 ✅ 제너레이터 내부에서 상태 유지 가능
실시간 데이터 처리 어려움 send()를 사용하면 가능
예외 처리 try-except 필요 throw()를 활용 가능

제너레이터와 send()를 활용하면 단순한 이터레이터를 넘어, 상태를 유지하는 강력한 코루틴을 만들 수 있다.

실시간 데이터 처리, 상태 머신, 이벤트 기반 시스템 등에 활용할 수 있다.

기존의 함수형 프로그래밍보다 훨씬 더 유연한 흐름 제어가 가능하다.

7. 결론: send()를 활용하면 제너레이터가 훨씬 강력해진다!

 

많은 개발자가 yield를 단순한 반환 용도로만 사용하지만,

👉 send()를 활용하면 제너레이터와 외부 데이터 간의 상호작용이 가능해진다.

👉 상태를 유지하는 코루틴을 만들 수 있으며, 실시간 데이터 처리에도 활용할 수 있다.

 

🔥 제너레이터를 단순히 next()로만 사용하지 말고, send()를 활용해 더욱 강력한 프로그램을 만들어보자! 🚀

+ Recent posts