web-dev-qa-db-ja.com

asyncio待機可能オブジェクト-基本的な例

待ちに待ったオブジェクトの作り方を理解しようとしています。 ドキュメント からの定義は次のように述べています。

イテレータを返す__await__メソッドを持つオブジェクト。

その定義に基づいて、サンプルコードを作成しました。

_import asyncio

async def produce_list():
        num = await Customer()
        print(num)

class Customer(object):

    def __await__(self):
        return iter([1, 2, 3, 4])

loop = asyncio.get_event_loop()
loop.run_until_complete(produce_list())
_

私が期待した流れは次のとおりです。

  1. イベントループはproduce_list()を制御します。 produce_list()num = await Customer()の実行を断念します。
  2. Customer()が実行され、イテレータを返します。これは、イテレータの最初の値を返すためです。 Q1:ここでは理由が明確ではありませんnumイテレータ自体にはなりません。また、ここでsendを実行しているのは何ですか?
  3. 最後の値に達すると、イテレータに到達します。 _num = 4_コルーチンの実行はprint(num)に続き、値4を出力します。

私が得たもの:

_---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
~/workspace/dashboard/so_question_await.py in <module>()
     16 
     17 loop = asyncio.get_event_loop()
---> 18 loop.run_until_complete(produce_list())

/usr/lib/python3.5/asyncio/base_events.py in run_until_complete(self, future)
    464             raise RuntimeError('Event loop stopped before Future completed.')
    465 
--> 466         return future.result()
    467 
    468     def stop(self):

/usr/lib/python3.5/asyncio/futures.py in result(self)
    291             self._tb_logger = None
    292         if self._exception is not None:
--> 293             raise self._exception
    294         return self._result
    295 

/usr/lib/python3.5/asyncio/tasks.py in _step(***failed resolving arguments***)
    239                 result = coro.send(None)
    240             else:
--> 241                 result = coro.throw(exc)
    242         except StopIteration as exc:
    243             self.set_result(exc.value)

~/workspace/dashboard/so_question_await.py in produce_list()
      5 
      6 async def produce_list():
----> 7         num = await Customer()
      8         print(num)
      9 

RuntimeError: Task got bad yield: 1
_

ここで間違った概念は何ですか?

最後に、コルーチンの制御に戻るためのイベントとしてリストの反復を使用する例を探しています。

21

___await___は、コルーチンの基礎となるメカニズムが元々_yield from_構文に基づいているため、イテレーターを返します。実際には、___await___はiter(some_future)またはsome_coroutine.__await__()のいずれかを返します。待機するたびに異なる値を生成するオブジェクトを作成するために使用できます。この簡単な例を参照してください。

_import asyncio
import random

class RandomProducer:

    def __await__(self):
        return self.producer().__await__()

    async def producer(self):
        sleep = random.random()
        value = random.randint(0, 9)
        return await asyncio.sleep(sleep, result=value)

async def main():
    producer = RandomProducer()
    while True:
        print(await producer)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
_

あなたのコメントに答えるために:

すべてのコルーチンは最終的に_asyncio.sleep_を呼び出すことになりますか?

いいえ、_asyncio.sleep_は実際にはチェーンの終わりではありません。一番下にあるのは、常に未来が生み出されていることです。コルーチンチェーンは、イベントループに「この未来が結果をもたらしたら、私を起こしてください」と尋ねます。 _asyncio.sleep_の場合、_loop.call_later_を使用して、指定された時間が経過した後の将来の結果を設定します。ループは、コールバックをスケジュールするためのより多くのメソッドを提供します:_loop.call_at_、_loop.add_reader_、_loop.add_writer_、_loop.add_signal_handler_など。

Aiohttpなどの非同期ライブラリ。以前のコルーチンの存在に依存しないコードがどこかにあると思います。

シングルスレッドの同時実行性を実現するには、すべてのIO操作をイベントループに委任する必要があります。たとえば、aiohttpは loop.create_connection コルーチンに依存しています。 TCP接続 を管理します。

5
Vincent