Open5

pythonのコルーチン的なものを試したい

astkastk

例えばサーバに重めの処理を投げて待つだけ、という処理を並列して行うクライアントを書くときに、スレッドは要求する機能として強すぎる気がするので、JavaScriptのように非同期タスクを積む感じで実行できないかと探したら

https://docs.python.org/3/library/asyncio-task.html

を見つけたのでちょっと触ってみたい。Goのgoroutine的なものだといいな。

astkastk

とりあえずhello worldを動かす。

import asyncio

async def main():
  print('hello')
  await asyncio.sleep(1)
  print('world')

asyncio.run(main())

async functionの外でawaitを使おうとするとエラーになる。

asataka@tailmoon hello-python % ./main.py
  File "/Users/asataka/src/local/hello-python/./main.py", line 6
    await asyncio.sleep(1)
    ^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: 'await' outside async function
asataka@tailmoon hello-python % 
astkastk

sleepの代わりにasyncio.sleepを使うのは普通のsleepだとブロッキングになるからなのかな。

astkastk

以下のような書き方をしたときに並列で実行されるのはmain3だけになる。

async def say_after(delay: int, what: str):
    await asyncio.sleep(delay)
    print(what)

async def main1():
    await say_after(1, 'hello')
    await say_after(2, 'world')

async def main2():
    await asyncio.create_task(say_after(1, 'hello'))
    await asyncio.create_task(say_after(2, 'world'))

async def main3():
    task1 = asyncio.create_task(say_after(1, 'hello'))
    task2 = asyncio.create_task(say_after(2, 'world'))
    await task1
    await task2
astkastk

REPLで動かしながら何が返ってきているのか確認してみる。
asyncは構文だけどasyncioはパッケージなのがちょっとややこしいかもしれない。

async def say_after(delay: int, what: str):
    await asyncio.sleep(delay)
    print(what)
say_after(1, 'hello')
# <coroutine object say_after at 0x10bf43300>

import asyncio
asyncio.create_task(say_after(1, 'hello'))
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "/Users/asataka/.asdf/installs/python/3.10.1/lib/python3.10/asyncio/tasks.py", line 336, in create_task
#     loop = events.get_running_loop()
# RuntimeError: no running event loop

async functionを呼び出すとcoroutine objectというものが返ってくるらしい。
asyncio.create_taskをするとno running event loopとエラーが出るので、多分REPL環境では実行できないんだろう。