Pythonのジェネレータには .send()
, .throw()
, .close()
というメソッドがありコルーチンとして使うことができます(PEP 342)。
しかし、残念なことにジェネレータベースのコルーチンはPython 3.10での廃止が予定されているので、async def ベースのコルーチンに書き換えなければなりません。
・・・が、なぜか、具体的な書き換え方法はドキュメントに書かれていませんでした(ジェネレータベースのコルーチンを、@types.coroutine でラップして async ベースとして使う方法は書かれていましたが)。
書き換える方法
コルーチン関数の方は、単にdef
をasync def
に書き換えるだけです。
Generator-based | Async-based |
---|---|
def |
async def |
yield |
yield (変更なし) |
コルーチンと通信する部分は、頭にa
がついたメソッドをawait
で呼び出すようにします。
Generator-based | Async-based |
---|---|
c.send() |
await c.asend() |
c.throw() |
await c.athrow() |
c.close() |
await c.aclose() |
さらに、 コルーチンを呼び出す側の関数(それ自体はコルーチンではない)も、async コルーチンに書き換え、asyncio.run
で実行するようにします。
Generator-based | Async-based |
---|---|
caller() |
asyncio.run(caller()) |
書き換えの例
わざとらしいですが、こんな数値の合計値を随時返すコルーチン(sumup
)と、呼び出す関数(main
)があったとします。
def sumup():
n = 0
while True:
n += yield n
def main():
coroutine = sumup()
coroutine.send(None)
x = coroutine.send(1)
print(x)
x = coroutine.send(2)
print(x)
x = coroutine.send(3)
print(x)
coroutine.close()
main()
出力:
1
3
6
Bye!
書き換えるとこうなります。
import asyncio
async def sumup():
n = 0
while True:
n += yield n
async def main():
coroutine = sumup()
await coroutine.asend(None)
x = await coroutine.asend(1)
print(x)
x = await coroutine.asend(2)
print(x)
x = await coroutine.asend(3)
print(x)
await coroutine.aclose()
asyncio.run(main())