41.2 코루틴 바깥으로 값 전달하기

지금까지 코루틴 안에 값을 보내기만 했는데 이번에는 코루틴에서 바깥으로 값을 전달해보겠습니다. 다음과 같이 (yield 변수) 형식으로 yield에 변수를 지정한 뒤 괄호로 묶어주면 값을 받아오면서 바깥으로 값을 전달합니다. 그리고 yield를 사용하여 바깥으로 전달한 값은 next 함수(__next__ 메서드)와 send 메서드의 반환값으로 나옵니다.

  • 변수 = (yield 변수)
  • 변수 = next(코루틴객체)
  • 변수 = 코루틴객체.send(값)

그럼 코루틴에 숫자를 보내고, 코루틴은 받은 숫자를 누적해서 바깥에 전달해보겠습니다.

coroutine_producer_consumer.py

def sum_coroutine():
    total = 0
    while True:
        x = (yield total)    # 코루틴 바깥에서 값을 받아오면서 바깥으로 값을 전달
        total += x
 
co = sum_coroutine()
print(next(co))      # 0: 코루틴 안의 yield까지 코드를 실행하고 코루틴에서 나온 값 출력
 
print(co.send(1))    # 1: 코루틴에 숫자 1을 보내고 코루틴에서 나온 값 출력
print(co.send(2))    # 3: 코루틴에 숫자 2를 보내고 코루틴에서 나온 값 출력
print(co.send(3))    # 6: 코루틴에 숫자 3을 보내고 코루틴에서 나온 값 출력

실행 결과

0
1
3
6

코루틴에서 값을 누적할 변수 total를 만들고 0을 할당합니다. 그리고 x = (yield total)과 같이 값을 받아오면서 바깥으로 값을 전달하도록 만듭니다. 즉, 바깥에서 send가 보낸 값은 x에 저장되고, 코루틴 바깥으로 보낼 값은 total입니다. 그다음에 total += x와 같이 받은 값을 누적해줍니다.

def sum_coroutine():
    total = 0
    while True:
        x  = (yield total)    # 코루틴 바깥에서 값을 받아오면서 바깥으로 값을 전달
        total += x

코루틴 바깥에서는 co = sum_coroutine()과 같이 코루틴 객체를 생성한 뒤 next(co)로 코루틴 안의 코드를 최초로 실행하여 yield까지 코드를 실행하고, printnext(co)에서 반환된 값을 출력합니다. 그다음에 co.send로 숫자 1, 2, 3을 보내고, printco.send에서 반환된 값을 출력합니다.

co = sum_coroutine()
print(next(co))      # 0: 코루틴 안의 yield까지 코드를 실행하고 코루틴에서 나온 값 출력
 
print(co.send(1))    # 1: 코루틴에 숫자 1을 보내고 코루틴에서 나온 값 출력
print(co.send(2))    # 3: 코루틴에 숫자 2를 보내고 코루틴에서 나온 값 출력
print(co.send(3))    # 6: 코루틴에 숫자 3을 보내고 코루틴에서 나온 값 출력

참고로 nextsend의 차이를 살펴보면 next는 코루틴의 코드를 실행하지만 값을 보내지 않을 때 사용하고, send는 값을 보내면서 코루틴의 코드를 실행할 때 사용합니다.

이 코루틴의 동작 과정을 그림으로 살펴보겠습니다.

먼저 next(co)로 코루틴의 코드를 최초로 실행하면 x = (yield total)yield에서 total을 메인 루틴으로 전달하고 대기합니다. 그다음에 메인 루틴에서 print(next(co))와 같이 코루틴에서 나온 값을 출력합니다. 여기서는 total에 0이 들어있으므로 0을 받아와서 출력합니다.

그림 41-5 코루틴의 코드를 최초로 실행한 뒤 yield의 값을 받아와서 출력

그리고 co.send(1)로 1을 보내면 코루틴은 대기 상태에서 풀리고 x = (yield total)x = 부분이 실행된 뒤 total += x로 숫자를 누적합니다. 이 코루틴은 while True:로 반복하는 구조이므로 다시 x = (yield total)yield에서 total을 메인 루틴으로 전달하고 대기합니다. 그다음에 메인 루틴에서 print(co.send(1))과 같이 코루틴에서 나온 값을 출력합니다. 여기서는 total에 1이 들어있으므로 1을 받아와서 출력합니다.

그림 41-6 코루틴에 값을 보낸 뒤 결과를 받아서 출력

이런 과정으로 (yield total)이 바깥으로 전달한 값을 nextsend의 반환값으로 받고, send가 보낸 값을 x = (yield total)x가 받게 됩니다.

여기서는 yield를 사용하여 코루틴 바깥으로 값을 전달하면 nextsend의 반환값으로 받는다는 점만 기억하면 됩니다.

마지막으로 제너레이터와 코루틴의 차이점을 정리해보겠습니다.

  • 제너레이터는 next 함수(__next__ 메서드)를 반복 호출하여 값을 얻어내는 방식
  • 코루틴은 next 함수(__next__ 메서드)를 한 번만 호출한 뒤 send로 값을 주고 받는 방식

코루틴은 초보자가 처음부터 작성하기가 힘듭니다. 따라서 다른 사람이 만든 소스 코드(GitHub에 공개된 소스 코드 등)를 참고하여 학습하고, 조금씩 수정하면서 원하는 결과를 얻어내면 됩니다.

참고 | 값을 보내지 않고 코루틴의 코드 실행하기

값을 보내지 않으면서 코루틴의 코드를 실행할 때는 next 함수(__next__ 메서드)만 사용하면 됩니다. 잘 생각해보면 이 방식이 일반적인 제너레이터입니다.