← Назад к вопросам

Что позволяет делать генератор?

1.7 Middle🔥 221 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Генераторы в Python — назначение и возможности

Генератор — это функция, которая вместо возвращения всех значений сразу (return), выдаёт их по одному через yield. Это мощный инструмент, который позволяет писать более эффективный и читаемый код.

Основная идея

# Обычная функция — возвращает список
def get_numbers_list():
    result = []
    for i in range(5):
        result.append(i)
    return result  # Всё сразу в памяти

# Генератор — выдаёт по одному
def get_numbers_generator():
    for i in range(5):
        yield i  # Выдаёт одно значение, приостанавливает выполнение

print(get_numbers_list())      # [0, 1, 2, 3, 4]
print(get_numbers_generator()) # <generator object>

for num in get_numbers_generator():
    print(num)  # 0, 1, 2, 3, 4

Что позволяют делать генераторы

1. Экономия памяти

Генератор не хранит все значения в памяти одновременно, а вычисляет их по мере необходимости.

# Обычный подход — много памяти
def squares_list(n):
    return [i**2 for i in range(n)]

result = squares_list(1000000)  # Создаёт список из 1 млн элементов
print(result[0])  # 0

# Генератор — минимум памяти
def squares_gen(n):
    for i in range(n):
        yield i**2

gen = squares_gen(1000000)  # Объект генератора, практически без памяти
print(next(gen))  # 0 — вычислилось одно значение

2. Ленивое вычисление (Lazy evaluation)

Значения вычисляются только когда они нужны, а не заранее.

def process_large_file(filename):
    """Читает файл по строкам, не загружая весь файл в памяти"""
    with open(filename) as f:
        for line in f:  # f сам по себе генератор
            yield line.strip()

for line in process_large_file("huge_file.txt"):
    if "error" in line:
        print(line)  # Обрабатываем строку
    # Файл читается по мере необходимости

3. Упрощение асинхронного кода

Генераторы с yield позволяют писать асинхронный код синтаксически просто.

import asyncio

async def fetch_data():
    data = []
    for i in range(5):
        await asyncio.sleep(0.1)
        data.append(f"item_{i}")
    return data

# Со старым синтаксисом генераторов-корутин
def old_coroutine():
    result = yield from fetch_data()  # Было до async/await
    return result

4. Создание бесконечных последовательностей

Можно создавать потоки данных, которые работают бесконечно.

def infinite_counter():
    """Бесконечный счётчик"""
    i = 0
    while True:
        yield i
        i += 1

counter = infinite_counter()
for _ in range(5):
    print(next(counter))  # 0, 1, 2, 3, 4

# В памяти хранится только текущее число, а не весь счётчик

5. Трубопровод обработки данных (Pipeline)

Можно строить цепочки генераторов, где каждый преобразует данные.

def read_data(filename):
    """Читает строки из файла"""
    with open(filename) as f:
        for line in f:
            yield line.strip()

def filter_comments(lines):
    """Фильтрует строки без комментариев"""
    for line in lines:
        if not line.startswith("#"):
            yield line

def parse_numbers(lines):
    """Парсит числа из строк"""
    for line in lines:
        try:
            yield int(line)
        except ValueError:
            pass

# Цепочка обработки
pipeline = parse_numbers(filter_comments(read_data("data.txt")))

for number in pipeline:
    print(f"Processed: {number}")

# Каждая функция обрабатывает по одному элементу,
# минимум памяти, максимум эффективность

6. Состояние между вызовами

Генератор сохраняет своё состояние между вызовами yield.

def fibonacci():
    """Генератор последовательности Фибоначчи"""
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b  # Состояние сохраняется

fib = fibonacci()
for _ in range(7):
    print(next(fib))  # 0, 1, 1, 2, 3, 5, 8

7. Двусторонняя коммуникация с send()

Можно отправлять значения в генератор через send().

def echo():
    """Генератор, который получает и отправляет данные"""
    while True:
        value = yield
        print(f"Получено: {value}")

gen = echo()
next(gen)  # Запускаем генератор до первого yield
gen.send("Hello")  # Отправляем значение
gen.send("World")  # Отправляем ещё

8. Альтернатива циклам с состоянием

def iterate_with_progress():
    """Альтернатива сложному циклу с состоянием"""
    total = 100
    for i in range(total):
        progress = (i + 1) / total * 100
        yield {"current": i + 1, "progress": progress}

for item in iterate_with_progress():
    print(f"{item['current']}/100 ({item['progress']:.1f}%)")

Практический пример: обработка больших данных

def read_csv_generator(filename, chunk_size=100):
    """Читает CSV файл батчами, без загрузки в памяти"""
    batch = []
    with open(filename) as f:
        for line in f:
            batch.append(line.strip().split(","))
            if len(batch) == chunk_size:
                yield batch
                batch = []
        if batch:  # Последний батч
            yield batch

def process_data(filename):
    """Обработка больших CSV файлов"""
    for batch in read_csv_generator(filename, chunk_size=1000):
        # Обрабатываем батч из 1000 строк
        processed = [row for row in batch if row[0] != ""]
        print(f"Processed {len(processed)} rows")

process_data("huge_data.csv")
# Файл читается и обрабатывается батчами
# Памяти используется максимум для одного батча (1000 строк)

Сравнение: список vs генератор

# Список — всё в памяти
data_list = [i**2 for i in range(1000000)]  # ~50 МБ памяти
first = data_list[0]

# Генератор — ленивое вычисление
data_gen = (i**2 for i in range(1000000))  # ~1 КБ памяти
first = next(data_gen)

Встроенные генераторы в Python

range(100)  # Генератор чисел
open("file.txt")  # Генератор строк файла
enumerate([1, 2, 3])  # Генератор пар (индекс, значение)
zip([1, 2], ["a", "b"])  # Генератор кортежей
map(str, [1, 2, 3])  # Генератор преобразованных значений
filter(lambda x: x > 2, [1, 2, 3])  # Генератор отфильтрованных значений

Итог

Генераторы позволяют:

  • Экономить память при работе с большими данными
  • Ленивое вычисление — вычислять только при необходимости
  • Писать элегантный код с цепочками преобразований
  • Работать с бесконечными потоками данных
  • Упрощать асинхронный код через корутины

Это один из самых мощных и элегантных инструментов Python для работы с данными.