Асинхронные генераторы и итераторы
🔄 Введение: Синхронные 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())
💡 Когда использовать асинхронные генераторы?
- Работа с сетью — запросы к API, веб-скрейпинг.
- Базы данных — асинхронное чтение больших наборов данных.
- Стриминг данных — обработка данных по мере их поступления.
🛠️ Полезные трюки
Комбинация с 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.
Для более глубокого погружения в асинхронность рекомендую курс Данилы Бежина — там разбираются сложные кейсы и паттерны! 🚀