Преобразование из генератора на родные сопрограммы - PullRequest
4 голосов
/ 01 ноября 2019

Я использовал Python в течение многих лет, но лишь постепенно изучал более неясные возможности языка, так как большая часть моего кода предназначена для обработки данных. Генераторы на основе yield являются частью моего рутинного инструментария, и недавно я прочитал о сопрограммах. Я нашел пример, похожий на этот:

def averager():
    sum = 0.0
    n = 0
    while True:
        value = yield
        sum += value
        n += 1
        print(sum/n)

avg = averager()
next(avg) # prime the coroutine
avg.send(3)
avg.send(4)
avg.send(5)

, который печатает среднее значение отправленных ему значений. Я подумал, что что-то подобное может пригодиться в конвейерах обработки данных, поэтому я решил оставить это в голове. То есть, пока я не прочитал следующее уведомление в документации Python :

Поддержка сопрограмм на основе генератора устарела и планируется удалить в Python 3.10.

Очевидно, я хотел бы написать код, ориентированный на будущее, поэтому на данный момент, вероятно, бесполезно начинать изучение сопрограмм на основе генератора. Мой вопрос: Как реализовать этот пример с использованием собственных (asyncio) сопрограмм? Мне гораздо сложнее обернуть голову вокруг родного синтаксиса сопрограмм.

Хотяпытаясь найти ответ, я нашел связанный вопрос , в котором есть комментарий и ответ , которые в основном говорят: «Вы не можете сделать это с async, сделайте это с сопрограммами на yield основе ". Но если они уходят, будет ли какой-нибудь способ сделать это с сопрограммами в 3.10 +?

1 Ответ

1 голос
/ 02 ноября 2019

И вот приходят Асинхронные генераторы ...

Таким образом, у нас все еще есть эта мощность в асинхронном контексте.
Что касается теории - упомянутый PEP 525 дает отличное описание и, безусловно, заслуживает прочтения.
Я просто опубликую подготовленный иллюстративный пример (для асинхронного averager) с инициализацией и безопасным завершением в комплекте:

import asyncio

async def coro():
    print('running other coroutine in 3 sec ...')
    await asyncio.sleep(3)  # emulate working


async def averager():
    sum_ = n = 0
    while True:
        v = yield
        sum_ += v
        n += 1
        print(sum_ / n)
        await asyncio.sleep(0.1)


async def main():
    agen = averager()
    await agen.asend(None)
    print(agen.__name__, 'initialized ...')

    await agen.asend(3)
    print('another separate processing here ...')

    await coro()

    await agen.asend(4)
    await agen.asend(14)


loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())
    loop.close()

Вывод программы:

averager initialized ...
3.0
another separate processing here ...
running other coroutine in 3 sec ...
3.5
7.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...