← Назад к списку
ТехническаяPythonJunior

Что такое генераторы и чем они отличаются от обычных функций и списков?

Короткий ответ

  • Генератор — функция с yield, возвращает ленивый итератор
  • Значения вычисляются по одному при вызове next()
  • Состояние функции замораживается между yield
  • Экономит память: не держит всю коллекцию целиком
  • Генератор можно обойти только один раз
  • Генераторные выражения — (x for x in ...) вместо списка

Генератор лениво выдаёт значения по одному, сохраняя состояние между вызовами, что экономит память на больших последовательностях.

Как сказать вслух

пример ответа

Генератор — это функция с yield: при вызове она не выполняется сразу, а возвращает объект, который выдаёт значения по одному. Между выдачами состояние функции сохраняется. Главный плюс — экономия памяти: можно обрабатывать файл на гигабайты, не загружая его целиком. Важно помнить, что генератор одноразовый — второй раз по нему не пройтись.

Подробный ответ

Основной ответ

Функция с yield при вызове возвращает объект-генератор, реализующий протокол итератора (__iter__ и __next__). Тело начинает исполняться только при первом next() и приостанавливается на каждом yield, сохраняя локальные переменные и позицию. Это даёт ленивые вычисления: элементы создаются по запросу, память не растёт с размером данных. Генераторные выражения (x*2 for x in data) — компактная форма. Генератор исчерпывается один раз, после чего поднимает StopIteration. Дополнительно есть yield from для делегирования вложенному генератору и методы send()/close() — на их основе исторически строились корутины.

Ключевые моменты

  • Протокол итератора. Итерируемый объект отдаёт итератор через __iter__; итератор выдаёт значения через __next__ и поднимает StopIteration в конце.
  • Ленивость и память. Список на 10 млн элементов занимает память целиком, генератор — O(1): хранится только текущее состояние.
  • Одноразовость. После полного обхода генератор пуст; чтобы пройтись снова, нужно создать его заново.
  • yield from. Делегирует итерацию вложенному генератору и пробрасывает send/throw, упрощая композицию.

Практический контекст

В работе генераторы встречаются при чтении больших файлов построчно, стриминге ответов API, пайплайнах обработки данных и в ORM (QuerySet.iterator() в Django). Интервьюер проверяет понимание ленивости и типичный вопрос-ловушку: «что выведет повторный проход по генератору». Полезно показать, что вы знаете, когда генератор не нужен — если данные маленькие и нужны несколько раз, список проще.

Пример кода

def read_large_file(path):
    with open(path, encoding="utf-8") as f:
        for line in f:
            yield line.strip()

lines = read_large_file("big.log")
errors = (l for l in lines if "ERROR" in l)
for e in errors:
    print(e)  # файл читается построчно, память O(1)

Частые ошибки

  • Пытаются обойти генератор дважды и ждут те же значения
  • Говорят, что функция с yield «возвращает список»
  • Не могут объяснить разницу между итерируемым объектом и итератором

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru