4. 핑퐁 게임하듯 대화하는 함수: 코루틴 (coroutine)
방금 배운 제너레이터는 함수의 흐름을 일시 정지하고 밖에 있는 사람(우리가 쓴 메인 코드)에게 짠! 하고 “데이터를 꺼내주는” 멋진 재주(yield)를 갖고 있었습니다.
그런데 파이썬 개발자들은 문득 이런 재밌는 생각을 했습니다.
“함수 밖으로 데이터를 꺼내는 것뿐만 아니라, 거꾸로 일시 정지해 있는 함수 안으로 새 데이터를 쏙 밀어 넣을 수는 없을까?”
이 기발한 역발상으로 탄생한 것이 바로 핑퐁 게임처럼 밖과 안이 데이터를 주고받으며 대화하는 코루틴(Coroutine)입니다!
4.1. 받기만 하는 yield
제너레이터에서는 yield 값 형태로 응답을 밖으로 보냈었죠? 코루틴에서는 변수 = yield 형태로 바뀌어, 함수 밖에서 자기를 향해 던져주는 데이터를 두 팔 벌려 받습니다.
밖에서 이 일시 정지된 코루틴 함수 안으로 데이터를 던져 넣을 땐 send() 라는 강력한 메서드를 사용합니다.
def ping_pong_robot():
while True:
# 누군가 밖에서 나한테 데이터를 파싱해주면 그 값을 ball에 저장합니다.
ball = yield
print(f"야호! 로봇이 [{ball}]을 튕겨냅니다!")
# 코루틴 로봇 준비
robot = ping_pong_robot()
# [주의사항!] 로봇을 처음 가동시키기 위해선 준비 과정이 필요합니다.
# 처음에는 비어있는 값(None)을 던져서 yield 부분까지 도착하게 만들어야 합니다!
robot.send(None)
# 이제 로봇에게 핑퐁 공(데이터)을 던져 넣어봅시다!
robot.send("핑") # 야호! 로봇이 [핑]을 튕겨냅니다!
robot.send("퐁!") # 야호! 로봇이 [퐁!]을 튕겨냅니다!
robot.send("스매시!")# 야호! 로봇이 [스매시!]을 튕겨냅니다!
4.2 코루틴의 라이프 사이클 관리 (close, throw)
이 핑퐁 로봇은 계속 대기 상태(while True)에 있기 때문에 놔두면 프로그램이 끝날 때까지 멈춰있습니다. 친절하게 전원을 꺼주는 작업이 필요합니다.
close(): 코루틴을 강제로 종료시킵니다.throw(): 코루틴 안쪽으로 폭탄(예외/Error)을 던져 넣어 강제 예외 처리를 유발하게 만듭니다.
# 퇴근할 시간이 되면 로봇을 끕니다.
robot.close()
# 끈 로봇한테 또 데이터를 던지면?
# "더 이상 못 받아요!" 라는 StopIteration 에러가 터집니다.
# robot.send("막타") (에러 발생!)
4.3 핑퐁을 치며 계산 누적하기 (산출과 입력을 동시에!)
코루틴의 진짜 마법은 데이터를 받는 것(yield 왼쪽에 변수)과 동시에 다른 값을 밖으로 산출(yield 오른쪽에 결과값)할 수 있다는 점입니다.
반환받는값 = yield 산출할값 처럼 적으면, 나에게 들어온 공(입력)을 받아 내부에서 멋지게 가공한 다음, 다시 밖으로 스파이크(결과)를 날릴 수 있습니다.
def running_average_bot():
total = 0
count = 0
average = 0
while True:
# 시작 시 average(0)를 밖으로 주고 일시 정지합니다.
# 누군가 나에게 새 숫자를 send() 하면 그걸 new_number에 저장합니다.
new_number = yield average
# 내부 로직 가동!
total += new_number
count += 1
average = total / count
# 평균 계산기 가동
avg_bot = running_average_bot()
print(avg_bot.send(None)) # 준비 완료 후 최초 초기값(0) 반환
# 숫자들을 쭉 던져주면 그동안의 평균을 계산해서 바로바로 응답합니다!
print(avg_bot.send(10)) # 평균 10.0
print(avg_bot.send(20)) # 평균 15.0
print(avg_bot.send(90)) # 평균 40.0
이 코루틴의 특유의 “정지-응답-재개-통신” 구조는 훗날 서버 네트워크 접속, 데이터 동시 다운로드 같은 아주 복잡한 일(비동기 프로그래밍, async/await)을 매끄럽게 돌아가게 해주는 핵심 심장부가 됩니다!