「PEP 0492-非同期構文と待機構文を使用したコルーチン」の表面的な外観から、最初に「なぜこれが必要なのか」という疑問が残りました。 コルーチンは拡張ジェネレーターに十分に実装されており、一見するとyield fromをawaitに置き換え、デコレーターがコルーチンをasyncで作成しているように見えるかもしれません。 ここでは、これはすべてasyncioモジュールで使用するためだけに行われているという感覚を加えることができます。
しかし、これはもちろんそうではありません。トピックはより深く、より興味深いものです。
コルーチン
主なことは、おそらく、Pythonのコルーチンが特別なネイティブコルーチンオブジェクトであり、何らかの特別に設計されたジェネレーターなどではないことです。 このオブジェクトには、操作するための標準ライブラリのメソッドと機能があります。 つまり、現在は、言語の一部として定義されているオブジェクトです。
待つ
残念ながら、この新しいキーワードが導入されたものについて、ドキュメントとPEPで簡単な定義を見つけることができませんでした。 自分でそれを定式化しようと思います。awaitキーワードは、次の式が実行されるときに、現在のコルーチンから別のコルーチンまたは実行のメインスレッドに切り替えることができることを示します。
したがって、 待機後の式も単純ではなく、 待機可能なオブジェクトである必要があります。
待望のオブジェクト
待機可能オブジェクトには3つのオプションがあります。
- 別のコルーチン、つまりネイティブコルーチンオブジェクト。 これは連想させるもので、 yield fromを使用してyieldで別のジェネレーターが呼び出される場合と同様に実装されているようです。
- types.coroutine()デコレータを使用して作成されたジェネレータベースのコルーチン 。 これは、コルーチンがジェネレーターに基づいて実装される開発との互換性を確保するためのオプションです。
- イテレータを返す__await__マジックメソッドを実装する特別なオブジェクト。 この反復子を使用して、コルーチン実行の結果が実現されます。
少なくともこれを書いている時点では、PEPまたはドキュメントのいずれかに、 待機可能なオブジェクトを記述する方法の例が見つかりませんでした。 これは以下で修正されます。)
非同期
PEPには、「Why ...」および「Why not ...」という見出しのある12の段落があります。 それらのほとんどすべては、このキーワードがこのように使用されている理由の問題に専念しており、そうでない場合はそうではありません。 実際、 async defはコードで奇妙に見え、「pythonic way」のトピックに関する考えを引き起こしますか? 一方で、の非同期および非同期もあるため 、より包括的な全体像を望んでいたことは明らかです。
- async def- ネイティブコルーチン関数を定義します 。その結果は、まだ起動されていないネイティブコルーチンコルーチンオブジェクトを呼び出します。
- async for-ループで使用されるイテレータが次の値を受け取ったときに、現在のコルーチンから実行を切り替えることができることを決定します。 イテレータオブジェクトには、標準のマジックメソッドの代わりに__iter__および__next__ 、メソッド: __aiter__および__anext__があります。 機能的には似ていますが、定義からわかるように、彼らは自分の体でawaitを使用できます。
- async with-コンテキストブロックに入って終了するときに、現在のコルーチンから実行の切り替えがあるかもしれないことを決定します。 非同期ジェネレーターと同様に、マジックメソッド__enter__および__exit__の代わりに 、機能的に類似した__aenter__および__aexit__を使用する必要があります。
PEPの定義では、マジックメソッドで「非同期コード」を呼び出すことができると書かれています。 「現在のコルーチンから実行を切り替える」の方がより適切なオプションのようです。 「非同期コード」という用語を使用すると、「非同期コード」がコールバック関数に実装されることが多いため、誤解を招く可能性がありますが、これはわずかに異なるトピックです。
ドキュメントとPEPで非同期イテレーターとコンテキストマネージャーを使用する例で十分であり、ユースケースは一般的に理解可能であり、すべてが論理的です。 1つだけが明確ではありません-なぜ `async def`を使用して宣言されているため、異なる名前のマジックメソッドのバージョンを使用するのか。 どうやら、これは実装の機能に関連するもので、別の説明はありません。
調理方法は?
言語またはライブラリの新しい機能を学ぶことは、それをどのように、どこで使用するかという問題にすぐにかかっています。 私の意見では、より深い研究は実用的な例を続ける価値があります。 私にとって、トピックがコルーチン、非同期などに関連している場合、そのような実用的な例は、イベント駆動型アプローチを使用してHellowordを作成することです。 タスクの文言は次のとおりです。「スリープ機能の呼び出しは、コルーチンの実行を一定時間停止する必要があります。」
コルーチンとイベント駆動型は完全に組み合わされていますが、他の記事で詳しく説明していますが、なぜそう思うのでしょうか。 そして、この種の例は、コルーチンを使用することのほとんどすべての可能性とニュアンスを私たちによく示します。
実行のメインスレッドで実行されるイベントディスパッチャがあり、予想されるイベントが発生するとコールバック関数を呼び出すとします。 次に、実際の実装は次のようになります。
- スリープ関数は、指定された時間が経過するとコールバック関数を呼び出すようにイベントマネージャーを構成します。 その後、制御を実行のメインスレッド(つまり、ディスパッチャー)に切り替えます。
- ディスパッチャに転送されたコールバック関数は、指定された時間が経過した後に呼び出されます。 いくつかの有用な情報を転送してコルーチンに切り替えます。
次のようにエンコードできます。
from time import time from collections import deque from tornado.ioloop import IOLoop current = deque() class sleep(object): def __init__(self, timeout): self.deadline = time() + timeout def __await__(self): def swith_to(coro): current.append(coro) coro.send(time()) IOLoop.instance().add_timeout(self.deadline, swith_to, current[0]) current.pop() return (yield) def coroutine_start(run, *args, **kwargs): coro = run(*args, **kwargs) current.append(coro) coro.send(None) if __name__ == '__main__': async def hello(name, timeout): while True: now = await sleep(timeout) print("Hello, {}!\tts: {}".format(name, now)) coroutine_start(hello, "Friends", 1.0) coroutine_start(hello, "World", 2.5) IOLoop.instance().start()
ご覧のとおり、コードは短く、適度に明確で、ところで、動作しています。 その主なポイントについて説明します。
- イベントディスパッチャとして、 tornado.ioloop.IOLoopが使用されました。
- スリープクラスは待機可能なオブジェクトを実装します。その機能は、指定された時間後にコールバックを呼び出すように設定した後、イベントマネージャーに制御を転送することです。
- コールバック関数はクロージャーとして定義されますが、この場合は何の役割も果たしません。 その目的は、現在の時刻を送信して、実行をコルーチンに単純に戻すことです。 実行をコルーチンに切り替えるには、 sendメソッドまたはthrowメソッドを呼び出して、例外をスローして切り替えます。
- coroutine_start関数の目的は、ファクトリ関数を呼び出して実行することによりコルーチンを作成することです。 コルーチン送信メソッドの最初の呼び出しは、 Noneパラメーターを使用する必要があります-これはコルーチンを起動します
- hello関数自体は簡単です。 明らかかもしれませんが、明確にする価値があると思います。 この関数はコルーチンではありません! コルーチン(ファクトリー関数)を作成して返すこの関数は、ジェネレーターを作成して返す関数に似ています。
このアイデアの開発:「非同期/待機コルーチンおよびイベント駆動」は、 このリンクで表示できます。 まだ生ですが、「タイムアウト」イベントの実証された切り替えに加えて、「I / O ready」および「system sygnal」イベントのコルーチンの切り替えが実装されています。 デモとして、非同期エコーサーバーの例があります。
結論として
何らかの形のコルーチンがかなり前から利用可能でしたが、「それらと遊ぶための公式ルール」はありませんでした。 現在、これらの「ゲームのルール」は定義および修正されており、これがPythonで非同期プログラミングメソッドをより広く使用する正当な理由となります。