@bromzh
Drugs-driven development

Как в python 3.4 вернуть несколько значений из сопрограммы (корутины)?

Если взять обычные генераторы, то реализовать подобное просто:
def g():
    yield 1
    some_actions()
    yield 2
    more_actions()
    ...
    yield 42

for result in g():
    print(result)

Плюс, можно через send() отправлять в такой генератор значения, например так:
def g():
    v = 0
    while True:
        v = yield v * 2
        if v > 100:
            break

gen = g()
next(gen)
r = 1
l = []
while True:
    try:
        r = gen.send(r)
        l.append(r)
    except StopIteration:
        break
# l -> [2, 4, 8, 16, 32, 64, 128]


Есть ли возможность так же делать для корутин из нового модуля asyncio? Собственно в функции будут исполняться несколько операций и хотелось бы при завершении каждого шага возвращать через yield некие данные в использующую этот генератор функцию. Если в корутине написать просто yield без from, то возникает RuntimeError('Task got bad yield'). Можно конечно каждое действие объявить как корутину и запускать такие задачи списком:
tasks = [
    asyncio.async(t1()),
    asyncio.async(t2())
]
loop.run_until_complete(asyncio.wait(tasks))

Однако тогда непонятно, как достать результат выполнения каждой функции.
  • Вопрос задан
  • 4022 просмотра
Решения вопроса 1
@bromzh Автор вопроса
Drugs-driven development
Ответ подсказали в другом месте. В общем, можно использовать для таких целей очередь:
import random
import asyncio

@asyncio.coroutine
def coro(que: asyncio.Queue):
    res = 0

    t = random.random() * 3
    yield from que.put('start 1')
    yield from asyncio.sleep(t)
    yield from que.put('value 1 = {}'.format(t))
    res += t

    t = random.random() * 3
    yield from que.put('start 2')
    yield from asyncio.sleep(t)
    yield from que.put('value 2 = {}'.format(t))
    res += t

    t = random.random() * 3
    yield from que.put('start 3')
    yield from asyncio.sleep(t)
    yield from que.put('value 3 = {}'.format(t))
    res += t

    yield from que.put(None)
    return res

@asyncio.coroutine
def run(que: asyncio.Queue):
    while True:
        res = yield from que.get()
        if res is None:
            break
        print(res)

q = asyncio.Queue()
asyncio.async(run(q))
asyncio.async(coro(q))
asyncio.get_event_loop().run_forever()
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
OlegWock
@OlegWock
Python, Java+Android, Frontend
Стоит добавить (мало ли кто еще сюда зайдет), что в Python 3.6 появились асинхронные генераторы и теперь асинхронные функции могут использовать выражение yield
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы