Что такое асинхронный генератор в Python?

Ответ

Асинхронный генератор — это функция, которая сочетает в себе поведение генератора (yield) и асинхронной корутины (async/await). Это позволяет создавать итераторы, которые могут приостанавливать свое выполнение не только для выдачи значения, но и для ожидания завершения асинхронной операции.

Объявляется с помощью async def и использует yield для возврата значений.

Ключевые особенности:

  • Итерация: Перебор значений осуществляется с помощью цикла async for.
  • Асинхронные вызовы: Внутри генератора можно использовать await для вызова других корутин.
  • Отложенные вычисления: Как и обычные генераторы, они производят значения «лениво», по одному за раз.

Пример:

import asyncio

# Асинхронный генератор, который имитирует
# получение данных из сети с задержкой.
async def fetch_data_stream(count: int):
    for i in range(count):
        # Имитация асинхронного I/O-запроса
        await asyncio.sleep(0.5)
        yield f"Chunk #{i + 1}"

async def main():
    print("Starting data stream...")
    # Используем async for для итерации по асинхронному генератору
    async for chunk in fetch_data_stream(3):
        print(f"Received: {chunk}")

asyncio.run(main())

Основное отличие от обычного генератора: Возможность выполнять await между операциями yield, что идеально подходит для работы с потоковыми данными, веб-сокетами или постраничной загрузкой из API.

Ответ 18+ 🔞

Слушай, а вот есть же такая штука — асинхронный генератор. Ну, это типа обычный генератор, который умеет yield, но при этом ещё и асинхронный, как корутина, со своими async/await. Представь: он может не только по одному значению выдавать, но и между выдачами спокойно ждать, пока какая-нибудь асинхронная хуйня завершится. Вообще удобно, блядь.

Что он умеет, этот хитрожопый зверь:

  • Итерироваться — но только через async for, обычный for тут обосрётся и ничего не поймёт.
  • Ждать асинхронщину — внутри можно await впендюрить сколько душе угодно.
  • Лениво работать — как и любой приличный генератор, не грузит всё в память сразу, а выдаёт по кусочку, когда попросят.

Вот смотри, как это выглядит на практике:

import asyncio

# Асинхронный генератор, который прикидывается, что тянет данные из сети
async def fetch_data_stream(count: int):
    for i in range(count):
        # Притворяемся, что ждём ответ от сервера
        await asyncio.sleep(0.5)
        yield f"Chunk #{i + 1}"

async def main():
    print("Starting data stream...")
    # Итерируемся по нему через async for — по-другому нихуя не получится
    async for chunk in fetch_data_stream(3):
        print(f"Received: {chunk}")

asyncio.run(main())

И главное, блядь, отличие от простого генератора: он между выдачами значений может вставить await и спокойно ждать, пока там сокет ответит или база данных отпишется. Идеально для стримов, веб-сокетов или когда тебе нужно постранично данные из API тащить, не блокируя весь мир. Ёпта, гениально же!