Может ли сопрограмма Python быть реализована без await или yield? - PullRequest
3 голосов
/ 07 марта 2019

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

import asyncio

async def coroutine():
    count = 0
    while count < 3:
        count += 1
        print(count)
        await asyncio.sleep(1)

loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine())
loop.close()

Результат:

1
2
3

Я заметил, что мы можем реализовать объект Coroutine, реализовав __await__(https://docs.python.org/3.6/reference/datamodel.html#awaitable-objects). Так что я могу успешно удалить await.

import asyncio

class Generator():
    def __await__(self):
        count = 0
        while count < 3:
            count += 1
            print(count)
            yield from asyncio.sleep(1)

loop = asyncio.get_event_loop()
loop.run_until_complete(Generator())
loop.close()

Наконец, я хочу реализовать итератор без выхода, как это:

import asyncio

class Iterator():

    def __init__(self):
        self.count = 0

    def __iter__(self): return self

    def __await__(self): return self

    def __next__(self):
        if self.count < 3:
            self.count += 1
            print(self.count)
            return next(asyncio.sleep(1))
        else:
            raise StopIteration()

loop = asyncio.get_event_loop()
result = loop.run_until_complete(Iterator())
loop.close()

Но это не такне работает. Он останавливается после того, как показывает «1».

Я знаю, что это не имеет никакого практического значения, но я хочу знать, как правильно понимать asyncio. Могу ли я реализовать сопрограмму без ожидания или выхода«Если так, то как это сделать?» Я протестировал это на Python 3.6.7.

1 Ответ

1 голос
/ 07 марта 2019

В то время как сопрограммы реализуются с использованием генераторов , сопрограммы не являются генераторами :

import asyncio
from typing import Generator


def gen():
    yield 1


async def coro():
    pass


print(isinstance(gen(), Generator))   # True
print(isinstance(coro(), Generator))  # False

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

loop.run_until_complete ожидает получить что-то ожидаемое, как asyncio сопрограмма или будущее.Вы пытаетесь передать итератор - другой объект с другим поведением.


Короче говоря, если вы хотите реализовать сопрограмму, совместимую с asyncio:

  • определите функции с помощью async def
  • или реализуйте __await__ магический метод

и прочитайте asyncio doc для примеров.

Если вы хотитеЧтобы понять общую идею о том, как сопрограммы работают на очень низком уровне, вы можете посмотреть это отличное видео , где Дэвид Бизли (JB) реализует сопрограмму и цикл обработки событий с нуля.

...