В Python додадуть асинхронні генератори

5 хв. читання

В Python 3.5 була додана можливість асинхронного програмування з використанням async&await. Тепер у пітон пропонують додати асинхронні генератори.

Звичайні генератори (представлені в PEP 255) дозволили елегантно писати функції, що покроково обробляли дані та вели себе як ітератори.

Але зараз немає аналогічної концепції для асинхронного for (async for). Саме таку концепцію було запропоновано в PEP 525. Зазначається, що саме такий підхід забезпечує подвійний приріст продуктивності в порівнянні з використанням асинхронних ітераторів.

Ось так, наприклад, можна написати ітератор, що повертає числа з заданим інтервалом:

class Ticker:
    """Повертає числа від 0 до `to` кожні `delay` секунд."""

    def __init__(self, delay, to):
        self.delay = delay
        self.i = 0
        self.to = to

    def __aiter__(self):
        return self

    async def __anext__(self):
        i = self.i
        if i >= self.to:
            raise StopAsyncIteration
        self.i += 1
        if i:
            await asyncio.sleep(self.delay)
        return i

А ось те ж саме, але використовуючи нову концепцію асинхронних генераторів:

async def ticker(delay, to):
    """Повертає числа від 0 до `to` кожні `delay` секунд."""
    for i in range(to):
        yield i
        await asyncio.sleep(delay)

Заманливо, чи не так?

Специфікація

В Python генератором є люба функція, що має один або більше yield:

def func():            # звичайна функція
    return

def genfunc():         # генератор
    yield

Пропонується використовувати схожу концепцію і для асинхронних генераторів:

async def coro():      # звичайна співпрограма
    await smth()

async def asyncgen():  # асинхронний генератор
    await smth()
    yield 42

Результатом виклику такого генератору буде повернення об'єкту асинхронного генератора, що реалізує інтерфейс асинхронного ітератора, що описаний тут: PEP 492.

Зауважте, що при використанні не пустого return в такому генераторі буде викликано SyntaxError.

Зворотня сумісність

Новий синтаксис повністю сумісний з минулими версіями Python. При оголошенні в Python 3.5 async def функції, що містить в своєму тілі yield буде викликано SyntaxError. В Python 3.6 такий синтаксис є повністю допустимим.

Ще декілька слів про продуктивність

Відносно звичайних генераторів

При використанні CPython (еталонна реалізація інтепретатора Python) асинхронні генератори працюють так само швидко як і звичайні (був використаний таки міні бенчмарк, як нижче).

def gen():
    i = 0
    while i < 100000000:
        yield i
        i += 1

list(gen())

Відносно асинхронних ітераторів

Наступний міні-бенчмарк, показує, що асинхронні генератори приблизно в 2.3 рази швидше, ніж та сама реалізація з асинхронними ітераторами. А ще вони виглядають лаконічніше.

N = 10 ** 7

async def agen():
    for i in range(N):
        yield i

class AIter:
    def __init__(self):
        self.i = 0

    def __aiter__(self):
        return self

    async def __anext__(self):
        i = self.i
        if i >= N:
            raise StopAsyncIteration
        self.i += 1
        return i

Інші аспекти

Асинхронні генератори списків, кортежів та словників

Не дивлячись на схожу назву, це зовсім різні концепції і вона повинна бути описана в окремому PEP.

Асинхронний yield from

Реалізувати yield from в теорії можливо, але це потребує дуже сильної переробки всієї концепції генераторів. Да і немає потреби в написанні механізму для реалізації ще одного протоколу для співпрограм, що працює поверх звичайних співпрограм.

Трошки прикладів

Ось приклад, що ілюструє роботу асинхронних генераторів. Він друкує числа від 0 до 9 з інтервалом в одну секунду.

async def ticker(delay, to):
    for i in range(to):
        yield i
        await asyncio.sleep(delay)


async def run():
    async for i in ticker(1, 10):
        print(i)


import asyncio
loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(run())
finally:
    loop.close()

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.7K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація