Python コルーチンの内部実装(send, throw, close)

コルーチンは send, throw, close メソッドで外部から制御できます。これらを使うと、双方向通信やエラーハンドリングが可能になります。

send:値を送り込む

yield 式は値を受け取れます。send で値を渡すと、yield 式の評価結果になります。

def accumulator():
    total = 0
    while True:
        value = yield total
        if value is None:
            break
        total += value

coro = accumulator()
next(coro)            # ジェネレータを起動(最初の yield まで進む)
print(coro.send(10))  # 10
print(coro.send(20))  # 30
print(coro.send(5))   # 35

最初の next() は send(None) と同等で、ジェネレータを最初の yield まで進めます。

throw:例外を投入する

throw でコルーチン内部に例外を発生させられます。

def coro_with_error_handling():
    try:
        while True:
            value = yield
            print(f"Received: {value}")
    except ValueError as e:
        print(f"Error handled: {e}")
        yield "recovered"

c = coro_with_error_handling()
next(c)
c.send(1)                          # Received: 1
result = c.throw(ValueError, "something wrong")
print(result)                      # recovered

close:コルーチンを終了する

close は GeneratorExit を投入してコルーチンを終了させます。

def resource_manager():
    print("Acquiring resource")
    try:
        while True:
            yield
    finally:
        print("Releasing resource")

c = resource_manager()
next(c)      # Acquiring resource
c.close()    # Releasing resource

finally ブロックでクリーンアップ処理が実行されます。

状態マシンの実装例

def state_machine():
    state = "IDLE"
    while True:
        event = yield state
        if state == "IDLE" and event == "start":
            state = "RUNNING"
        elif state == "RUNNING" and event == "stop":
            state = "IDLE"

sm = state_machine()
print(next(sm))         # IDLE
print(sm.send("start")) # RUNNING
print(sm.send("stop"))  # IDLE

send, throw, close を活用すると、コルーチンを協調的なタスクとして柔軟に制御できます。