PEP (Python Enhancement Proposal) #0492によると、Python 3.5はasync/await構文を使った継続をサポートする予定だ。提案は継続をネイティブな言語機能とし、「一般的で親しみやすい非同期プログラミングのメンタルモデルを構築する」ことを狙っている。
新しく提案された継続を宣言するための構文は次のようなものだ。
async def read_data(db):
pass
具体的に言うと、async
は、たとえawait
式を含んでいなくても、関数をコルーチンとして振舞うようにするキーワードだ。この関数を実行すると、コルーチンオブジェクトが返ってくる。
コルーチン本体では、await
キーワードを使うことで、その式にコルーチンの実行をサスペンドさせ、処理が完了するのを待たせることができる。
async def read_data(db):
data = await db.fetch('SELECT ...')
...
ジェネレータ拡張のおかげで、Pythonでもコルーチンを使うことができる。このPythonジェネレータはyield
やyield from
文が本体に登場すると、コルーチンとして扱う。
ジェネレータベースのコルーチンを使った例を以下に示す。
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator()
>>> for i in mygenerator:
... print(i)
0
1
4
このコードでは、ジェネレータをfor
ループで呼び出すたびに、ジェネレータのfor
ループから新しい値が返される。
await
の使用例はPEP #0492にある。
コルーチンの新しい提案には、ジェネレータとコルーチンを明確に分離するという目的がある。これによって次のようなメリットが期待されている。
- 同じ構文を共有していないため、新しい開発者が2つの概念を理解しやすくなる。
- リファクタリング中にうっかりコルーチンから
yield
文が取り除かれ、コルーチンがジェネレータとして扱われてしまうという「不明瞭なエラー」の原因を取り除く
async/await
構文を使うことで、開発者はシーケンシャルにコードを書けるが、コンパイラはこれを一連のコルーチンで実装する。これによって並列実行を効果的に行うことができる。先ほどの例に戻ると、async/awaitを使うと、まるで各文がブロックして結果が利用可能になるまで待つかのように、複数のawait
文をシーケンシャルに書くことができるが、実際にはブロックすることはない。
async def read_data(db):
data = await db.fetch('SELECT ...')
if (data...)
await api.send(data ...')