LoginSignup
5
4

More than 5 years have passed since last update.

ジェネレータベースのコルーチンを async で書き換えるにはどうすればいいか?

Last updated at Posted at 2019-02-17

Pythonのジェネレータには .send(), .throw(), .close() というメソッドがありコルーチンとして使うことができます(PEP 342)。

しかし、残念なことにジェネレータベースのコルーチンはPython 3.10での廃止が予定されているので、async def ベースのコルーチンに書き換えなければなりません。

・・・が、なぜか、具体的な書き換え方法はドキュメントに書かれていませんでした(ジェネレータベースのコルーチンを、@types.coroutine でラップして async ベースとして使う方法は書かれていましたが)。

書き換える方法

コルーチン関数の方は、単にdefasync 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())
5
4
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
5
4