asyncio awaitable object - базовый пример
Я пытаюсь понять, как сделать ожидаемый объект. В определении из документации указано:
Объект с методом __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())
Поток, который я ожидал, был:
-
produce_list()
события дает управление для produce_list()
. produce_list()
выполнение при num = await Customer()
. -
Customer()
выполняется и возвращает итератор. Какой из них возвращает первое значение в итераторе. Q1: неясно, почему num
не становится самим итератором. Также, что делает send
здесь? - Как только последнее значение достигнет итератора.
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
Какие понятия я получил здесь неправильно?
В конце я ищу пример, который использует итерацию через список как событие, чтобы вернуться к управлению сопрограммой.
Ответы
Ответ 1
__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
на самом деле не конец цепи. В самом низу всегда дается будущее: цепочка coroutine задает цикл событий "пожалуйста, разбудите меня, когда это будущее будет иметь результат". В случае asyncio.sleep
, он использует loop.call_later
чтобы установить результат будущего через определенное количество времени. Цикл предоставляет больше методов для планирования обратных вызовов: loop.call_at
, loop.add_reader
, loop.add_writer
, loop.add_signal_handler
и т.д.
Асинхронная библиотека, такая как aiohttp. Я предполагаю, что где-то есть код, который не полагается на существование предыдущих сопрограмм.
Все операции ввода-вывода должны завершиться делегированием цикла событий для достижения однопоточного параллелизма. Например, aiohttp полагается на loop.create_connection coroutine для управления TCP-соединением.