Асинхронные генераторы и итераторы

🔄 Введение: Синхронные vs. Асинхронные генераторы

Генераторы в Python — это мощный инструмент для ленивых вычислений. Они экономят память, возвращая значения по одному, а не все сразу. Но что, если нам нужно генерировать значения асинхронно? Например, при работе с сетью или базой данных? Здесь на помощь приходят асинхронные генераторы!

# Обычный генератор (синхронный)
def sync_gen():
    yield 1
    yield 2
    yield 3

# Асинхронный генератор (работает с async/await)
async def async_gen():
    yield 1
    yield 2
    yield 3

🏗️ Как работают асинхронные генераторы

Асинхронный генератор — это функция, которая использует async def и содержит ключевое слово yield. В отличие от обычных генераторов, их можно приостанавливать и возобновлять в асинхронном контексте.

import asyncio

async def countdown(n):
    while n > 0:
        yield n
        n -= 1
        await asyncio.sleep(1)  # Эмулируем долгую операцию

async def main():
    async for num in countdown(5):  # Используем async for!
        print(f"Осталось: {num} сек")

asyncio.run(main())

Вывод:

Осталось: 5 сек
Осталось: 4 сек
Осталось: 3 сек
Осталось: 2 сек
Осталось: 1 сек

🔄 Асинхронные итераторы

Асинхронный итератор — это объект, который реализует методы __aiter__ и __anext__. Он позволяет перебирать данные в асинхронном контексте.

class AsyncCounter:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __aiter__(self):
        return self

    async def __anext__(self):
        if self.current >= self.limit:
            raise StopAsyncIteration
        self.current += 1
        await asyncio.sleep(0.5)  # Имитируем задержку
        return self.current

async def main():
    async for number in AsyncCounter(3):
        print(f"Текущее число: {number}")

asyncio.run(main())

Вывод:

Текущее число: 1
Текущее число: 2
Текущее число: 3

🔄 Практический пример: Асинхронный HTTP-клиент

Давайте напишем асинхронный генератор, который будет получать данные из нескольких URL-адресов:

import aiohttp

async def fetch_urls(urls):
    async with aiohttp.ClientSession() as session:
        for url in urls:
            async with session.get(url) as response:
                yield await response.text()

async def main():
    urls = [
        "https://example.com",
        "https://python.org",
        "https://github.com"
    ]
    async for content in fetch_urls(urls):
        print(f"Получено данных: {len(content)} символов")

asyncio.run(main())

💡 Когда использовать асинхронные генераторы?

  1. Работа с сетью — запросы к API, веб-скрейпинг.
  2. Базы данных — асинхронное чтение больших наборов данных.
  3. Стриминг данных — обработка данных по мере их поступления.

🛠️ Полезные трюки

Комбинация с async for

Асинхронные генераторы можно использовать вместе с async for для удобного перебора:

async def async_range(n):
    for i in range(n):
        yield i
        await asyncio.sleep(0.1)

async def main():
    async for i in async_range(3):
        print(i)

Остановка генератора

Для остановки асинхронного генератора используется StopAsyncIteration (аналог StopIteration для обычных генераторов).


📌 Заключение

Асинхронные генераторы и итераторы — это мощный инструмент для работы с асинхронным кодом в Python. Они позволяют эффективно обрабатывать потоки данных без блокировки основного потока выполнения.

👉 Как закрепить знания? Попробуйте написать свой асинхронный генератор, например, для чтения файлов или работы с API.

Для более глубокого погружения в асинхронность рекомендую курс Данилы Бежина — там разбираются сложные кейсы и паттерны! 🚀

Скрыть рекламу навсегда

🎥 YouTube: программирование простым языком

Канал, где я спокойно и по шагам объясняю сложные темы — без заумных терминов и лишней теории.

Подходит, если раньше «не заходило», но хочется наконец понять.

▶️ Смотреть курсы на YouTube