LoginSignup
0
0

More than 1 year has passed since last update.

async/awaitなしでジェネレータをコルーチン的に動作させる

Posted at

Pythonでコーディングしていると、簡単な状態機械の実装など、たまに関数にコルーチン的な動作をしてほしくなるときがある。ただasync/awaitとかイベント駆動の仕組みをいちいち書きたくない場合も多い。

そんな時に、ジェネレータのyield文を使って値をやり取りすることで、イベント駆動の仕組みを作らなくても逐次的に関数(というかジェネレータ)と値をやり取りすることができるようになる。

ポイント

  1. ジェネレータとして問題のルーチンを定義する
    • 内部的にはyield文で値を返し、その返り値として値を受け取る。
  2. ジェネレータに対してiter()でイテレータを生成する。
    • イテレータのsend()メソッドで値を渡しつつ、その返り値としてジェネレータからの値を受け取る。
    • send()メソッドを使い始める前に、一度だけnext()を呼んでジェネレータを「起動」しないといけない。

サンプル

>>> def shifter():
...   # 一度next()を呼び出すことで、以下のルーチンが始まる。
...
...   # 呼び出し側からの最初のsend()メソッドで、状態の値を初期化
...   state = yield
...   while state is not None:
...      # 現在のstateの値を呼び出し側に返す。
...      # 呼び出し側はsend()で次のstateの値を入力できる
...      state = yield state
...   # send()でNoneを入力するかnext()を用いることで
...   # yield文の返り値はNoneになる
...
...   # 終了時の処理
...   yield "done"
...
>>> gshift = iter(shifter()) # ジェネレータからイテレータを生成。この状態ではまだ「起動」されていない
>>> next(gshift) # これで初めてジェネレータが起動する
#(ジェネレータ側の処理は、関数定義の5行目の`state = yield`のところで停止した状態)

# 次の呼び出しでsend()を用いることで5行目の`state = yield`に処理が戻る
# whileループの中の`state = yield state`のところで、現在のstateの値とともに処理が呼び出し側に返る
>>> gshift.send(0)
0 # 入力した`0`が返ってきた

>>> gshift.send(1)
1

>>> gshift.send('Hello')
'Hello'

>>> gshift.send(None) # next(gshift) でも同じことができる
'done' # 最後のyieldの値が返ってきた

# この段階で、ジェネレータの処理はすでに終了している
# この後にnext()やsend()を呼んでも、StopIterationが送出されるだけ
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0