Встречается на 33% собеседований по Python

Что такое генератор

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

Как это работает

Генератор — это функция или выражение, которое возвращает объект-генератор. Этот объект автоматически реализует протокол итератора, то есть имеет методы __iter__ и __next__. При каждом вызове next() генератор выполняет код до ближайшего оператора yield, возвращает значение и приостанавливает своё состояние. При следующем вызове выполнение продолжается с того же места.

Ключевое отличие от обычной функции: вместо return используется yield. return завершает функцию и возвращает одно значение, а yield приостанавливает выполнение, сохраняя локальные переменные, и позволяет возобновить его позже. Когда генератор исчерпан, при следующем вызове next() возникает исключение StopIteration.

Способы создания генераторов

Генераторное выражение

Выражение-генератор выглядит как списковое включение, но в круглых скобках:

python
my_generator = (i for i in range(10))

Такое выражение создаёт генератор, который лениво выдаёт числа от 0 до 9. В отличие от списка [i for i in range(10)], оно не хранит все элементы в памяти.

Функция-генератор

Функция, содержащая yield, автоматически становится генераторной функцией. При вызове она не выполняет тело, а возвращает объект-генератор:

python
def generator():
    for i in range(2):
        yield i

a = generator()
print(next(a))  # 0
print(next(a))  # 1
# print(next(a))  # StopIteration

Генератор можно использовать в цикле for, который сам обрабатывает исключение StopIteration:

python
for i in generator():
    print(i)
# 0
# 1

Пример: числа Фибоначчи

Классический пример — генератор чисел Фибоначчи. Без yield пришлось бы писать класс с методами __iter__ и __next__:

python
class FibonacciGenerator:
    def __init__(self):
        self.prev = 0
        self.cur = 1

    def __next__(self):
        result = self.prev
        self.prev, self.cur = self.cur, self.prev + self.cur
        return result

    def __iter__(self):
        return self

С yield реализация становится гораздо короче и понятнее:

python
def fibonacci():
    prev, cur = 0, 1
    while True:
        yield prev
        prev, cur = cur, prev + cur

for i in fibonacci():
    print(i)
    if i > 100:
        break

Подводные камни и особенности

  • Одноразовость: генератор можно обойти только один раз. После исчерпания он становится пустым, и повторный for по нему не даст результатов.
  • Ленивость: элементы создаются только при запросе, поэтому генераторы экономят память при работе с большими данными.
  • Генераторы — это итераторы: любой генератор является итератором, но не любой итератор — генератором.
  • Источники данных: генератор может брать элементы из коллекции, файла, сетевого соединения и преобразовывать их на лету. Примеры встроенных генераторов — range (генерирует арифметическую прогрессию) и enumerate (генерирует кортежи с индексом и элементом).

Когда использовать

Генераторы стоит применять, когда:

  • нужно обработать большой поток данных, не загружая его целиком в память;
  • требуется бесконечная последовательность (например, числа Фибоначчи);
  • нужно организовать конвейер обработки данных, где каждый шаг лениво преобразует элементы.

Коротко

  • Генератор — это итератор, который создаёт элементы на лету и обходится только один раз.
  • Создаётся через генераторное выражение (x for x in ...) или функцию с yield.
  • Экономит память и позволяет работать с бесконечными последовательностями.
  • После исчерпания генератор становится пустым, повторный обход невозможен.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы