Что такое генератор
Генератор в Python — это итератор, элементы которого можно обойти только один раз, поскольку они не хранятся в памяти целиком, а создаются на лету при каждой итерации. Генераторы — это удобный способ реализации итераторов, который позволяет экономить память и организовывать ленивые вычисления.
Как это работает
Генератор — это функция или выражение, которое возвращает объект-генератор. Этот объект автоматически реализует протокол итератора, то есть имеет методы __iter__ и __next__. При каждом вызове next() генератор выполняет код до ближайшего оператора yield, возвращает значение и приостанавливает своё состояние. При следующем вызове выполнение продолжается с того же места.
Ключевое отличие от обычной функции: вместо return используется yield. return завершает функцию и возвращает одно значение, а yield приостанавливает выполнение, сохраняя локальные переменные, и позволяет возобновить его позже. Когда генератор исчерпан, при следующем вызове next() возникает исключение StopIteration.
Способы создания генераторов
Генераторное выражение
Выражение-генератор выглядит как списковое включение, но в круглых скобках:
my_generator = (i for i in range(10))Такое выражение создаёт генератор, который лениво выдаёт числа от 0 до 9. В отличие от списка [i for i in range(10)], оно не хранит все элементы в памяти.
Функция-генератор
Функция, содержащая yield, автоматически становится генераторной функцией. При вызове она не выполняет тело, а возвращает объект-генератор:
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:
for i in generator():
print(i)
# 0
# 1Пример: числа Фибоначчи
Классический пример — генератор чисел Фибоначчи. Без yield пришлось бы писать класс с методами __iter__ и __next__:
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 реализация становится гораздо короче и понятнее:
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. - Экономит память и позволяет работать с бесконечными последовательностями.
- После исчерпания генератор становится пустым, повторный обход невозможен.
