Tornado 4.3ドキュメント翻訳:ユーザーズガイド-協程


訳者はTornado 4.3は2015年11月6日に発表され、このバージョンはPython3.5async/awaitキーワードを正式にサポートし、旧バージョンCPythonでTornadoをコンパイルしても同様にこの2つのキーワードを使用することができ、これは間違いなく進歩である.次に、これはPython2.6Python3.2をサポートする最後のバージョンであり、後続のバージョンでは互換性が削除されます.今ネット上でまだTornado4.3の中国語のドキュメントがなくて、だからもっと多くの友达にそれに接触して学ぶことができるように、私はこの翻訳プロジェクトを始めて、興味のある友达が一緒に翻訳に参加することができることを望んで、プロジェクトの住所はtornado-zh on Githubで、翻訳したドキュメントはRead the Docsの上で直接見ることができます.Issues or PRへようこそ.
きょうてい
Tornadoでは、非同期コードを書くことを推奨する.コンシステントは、チェーンコールバックの代わりにPythonのyieldキーワードを使用してプログラムを停止および復元する実行(geventに現れる軽量レベルのスレッド連携方式のように、コンシステントとも呼ばれる場合があるが、Tornadoにおけるすべてのコンシステントは明確なコンテキスト切替を使用し、非同期関数と呼ばれる).
コパスの使用は同期コードを書くように簡単であり、追加のスレッドを無駄にする必要はない.また、コンテキストの切り替えを減らすことで、同時プログラミングをより簡単にすることができる.
例:
    from tornado import gen

    @gen.coroutine
    def fetch_coroutine(url):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch(url)
        #  Python 3.3  ,  generator          
        #            .
        #    raise gen.Return(response.body).
        return response.body

Python 3.5:asyncawaitPython 3.5は、asyncおよびawaitのキーワード(これらのキーワードを使用する関数を「原生協程」とも呼ぶ)を導入する.Tornado 4.3から、yieldをベースとしたコラボレーションの代わりに使用できます.単純にasync def foo()を使用するだけで、関数定義時に@gen.coroutine装飾器の代わりにawaitをyieldの代わりに使用します.このドキュメントの他のセクションでは、古いバージョンのPythonと互換性のあるyieldのスタイルを引き続き使用しますが、asyncawaitが使用可能であれば、より高速に実行できます.

    async def fetch_coroutine(url):
        http_client = AsyncHTTPClient()
        response = await http_client.fetch(url)
        return response.body
awaitキーワードはyieldキーワードより機能が少ない.例えば、yieldを使用するスレッドでは、Futuresのリストを得ることができますが、原生スレッドでは、リストをtornado.gen.multiで包む必要があります.tornado.gen.convert_yieldedを使用して、yieldを使用して動作するコードをawaitを使用する形式に変換することもできます.
原生コンダクタンスは、特定のフレーム(例えば、装飾器を使用する、tornado.gen.coroutineまたはasyncio.coroutineなど)に著しく依存するが、すべてのコンダクタンスが他のものと互換性があるわけではない.1つのcoroutine runnerは、最初のコパスが呼び出されたときに選択する、awaitで直接呼び出されたすべてのコパスによって共有される.Tornadoのコーディネーター(coroutine runner)は設計上多用途であり、他のフレームワークからのawaitableオブジェクトを受け入れることができる.他のコンポジット実行時には多くの制限がある場合がある(例えば、asyncioコンポジット実行者は他のフレームからのコンポジットを受け入れない).これらの理由から、複数のフレームワークを組み合わせたアプリケーションは、Tornadoのコパス実行者を用いてコパススケジューリングを行うことを推奨する.Tornadoを使用するasyncioを実行するためのコンセンサスをスケジューリングするために、tornado.platform.asyncio.to_asyncio_futureアダプタを使用することができる.
どのように機能していますかyieldのキーワードを含む関数はジェネレータである.すべてのジェネレータは非同期です.それらを呼び出すと、実行済みの結果ではなくジェネレータオブジェクトが返されます.@gen.coroutineデコレラはyield式を介してジェネレータと交流する、1つの.Futureを返すことによって、コモンの呼び出し元と対話する.
次は、コーディネータの内部ループの簡単なバージョンです.
    # tornado.gen.Runner        
    def run(self):
        # send(x) makes the current yield return x.
        # It returns when the next yield is reached
        future = self.gen.send(self.next)
        def callback(f):
            self.next = f.result()
            self.run()
        future.add_done_callback(callback)

デザイナは、ジェネレータからFutureオブジェクトを受信、(ブロックされていない)このFutureオブジェクトの実行が完了するのを待ってから、「unwraps」というFutureオブジェクトを解き、結果をyield式の結果としてジェネレータに返す.ほとんどの非同期コードはFutureクラスに直接接触することはない.Futureが直ちに非同期関数を介してyield式に戻る場合を除く.
コパスの呼び出し方法
コンシステントは一般に異常を放出することはない:それらが放出するいかなる異常も.Futureに捕獲され、それが得られるまで捕獲される.これは、正しい方法でスレッドを呼び出すことが重要であることを意味します.そうしないと、無視されるエラーが発生する可能性があります.
    @gen.coroutine
    def divide(x, y):
        return x / y

    def bad_call():
        #          ZeroDivisionError    ,        
        #              .
        divide(1, 0)

ほとんどの場合、いずれの呼び出しコンシステントの関数もコンシステント自身である必要があり、呼び出し時にyieldキーワードを使用する.スーパークラスのメソッドを複写する場合は、ドキュメントを参照して、サポートされているかどうかを確認します(ドキュメントには、「1つのパスかもしれない」または「1つのFutureクラスを返すかもしれない」と書かれているはずです).
    @gen.coroutine
    def good_call():
        # yield      divide()     Future       
        yield divide(1, 0)

時には協程に「一労永逸」し、その結果を待たないことを望んでいるかもしれません.この場合、.IOLoop.spawn_callbackが呼び出しを担当するように.IOLoopを使用することを提案する.失敗した場合、.IOLoopはログに呼び出しスタックを記録します.
    # IOLoop       ,           .
    #              ,        
    # IOLoop        .
    IOLoop.current().spawn_callback(divide, 1, 0)

最後に、プログラムの最上位レベルで、.IOLoopがまだ実行されていない場合は、.IOLoopを起動し、コンセンサスを実行し、.IOLoop.run_syncメソッドを使用して.IOLoopを停止することができる.これは通常、バッチ向けのmain関数を起動するために使用されます.
    # run_sync()      ,           lambda   .
    IOLoop.current().run_sync(lambda: divide(1, 0))

コンシステントモード
結合callback.Futureの代わりにコールバックを使用する非同期コードと対話するために、.Taskクラスに呼び出しパケットを格納.これにより、コールバックパラメータが追加され、yield可能な.Futureが返されます.
    @gen.coroutine
    def call_task():
        #          some_function.
        #     Task   
        #   some_function(other_args, callback=callback)
        yield gen.Task(some_function, other_args)

ブロック関数の呼び出し
ブロック関数をコンシステントから呼び出す最も簡単な方法は、concurrent.futures.ThreadPoolExecutorを使用することです.これは、コンシステントと互換性のあるFuturesを返します.
    thread_pool = ThreadPoolExecutor(4)

    @gen.coroutine
    def call_blocking():
        yield thread_pool.submit(blocking_func, args)

パラレル
コプロセッサは、リストまたは辞書オブジェクトのそれぞれのFuturesを識別し、これらのFuturesを並列に待つことができる.
    @gen.coroutine
    def parallel_fetch(url1, url2):
        resp1, resp2 = yield [http_client.fetch(url1),
                              http_client.fetch(url2)]

    @gen.coroutine
    def parallel_fetch_many(urls):
        responses = yield [http_client.fetch(url) for url in urls]
        #     HTTPResponses       

    @gen.coroutine
    def parallel_fetch_dict(urls):
        responses = yield {url: http_client.fetch(url)
                            for url in urls}
        #         {url: HTTPResponse}

クロスアクセス
すぐにyieldよりも.Futureを保存する方が役に立つ場合がありますので、待つ前に他の操作を実行できます.
    @gen.coroutine
    def get(self):
        fetch_future = self.fetch_next_chunk()
        while True:
            chunk = yield fetch_future
            if chunk is None: break
            self.write(chunk)
            fetch_future = self.fetch_next_chunk()
            yield self.flush()

ループ
Pythonではforサイクルまたはwhileサイクルyield反復器でyieldの結果をキャプチャすることができないため、コヒーレントなサイクルは厄介である.逆に、ループ条件をアクセス結果から分離する必要があります.以下はMotorを使用する例です.
    import motor
    db = motor.MotorClient().test

    @gen.coroutine
    def loop_example(collection):
        cursor = db.collection.find()
        while (yield cursor.fetch_next):
            doc = cursor.next_object()

バックグラウンドで実行PeriodicCallback通常はコパスは使用する.逆に、1つのコプロセッサは、1つのwhile True:ループを含み、tornado.gen.sleepを使用することができる.
    @gen.coroutine
    def minute_loop():
        while True:
            yield do_something()
            yield gen.sleep(60)

    # Coroutines that loop forever are generally started with
    # spawn_callback().
    IOLoop.current().spawn_callback(minute_loop)

より複雑なサイクルに遭遇することがあります.例えば、前回のサイクル運転では、60+N秒がかかるが、Ndo_something()がかかる時間である.60秒ごとに正確に動作するには、上記のクロスモードを使用します.
    @gen.coroutine
    def minute_loop2():
        while True:
            nxt = gen.sleep(60)   #     .
            yield do_something()  #      .
            yield nxt             #       .