Что такое генератор в Python
Генератор в Python — это объект, который создаёт последовательность значений по запросу, а не хранит её целиком в памяти. Он производит элементы лениво, то есть по одному за раз, что позволяет экономить оперативную память и ускорять обработку больших объёмов данных. Генераторы реализуют протокол итератора и создаются с помощью функций, содержащих ключевое слово yield, или с помощью генераторных выражений.
Как это работает
Обычная функция при вызове выполняет код и возвращает результат, после чего её состояние теряется. Генераторная функция ведёт себя иначе: при вызове она не выполняет тело сразу, а возвращает объект-генератор. Каждый вызов next() (или итерация в цикле for) возобновляет выполнение функции с места последнего yield, сохраняя все локальные переменные между вызовами. Когда функция доходит до yield, она приостанавливается и возвращает значение; при следующем вызове продолжается со следующей строки.
def countdown(n):
while n > 0:
yield n # возвращает текущее значение и приостанавливается
n -= 1
for num in countdown(3):
print(num) # выведет 3, 2, 1Генераторное выражение — это компактный аналог спискового включения, но с круглыми скобками. Оно создаёт генератор без явного определения функции:
squares = (x * x for x in range(10))
print(sum(squares)) # 285Пример
Рассмотрим задачу чтения большого файла построчно. Если загрузить весь файл в список, память будет израсходована на все строки сразу. Генератор читает файл лениво, обрабатывая одну строку за раз:
def read_large_file(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
for line in file:
yield line.strip() # отдаём строку без перевода каретки
# Использование: обрабатываем строки по одной, не держа весь файл в памяти
for line in read_large_file('data.txt'):
process(line)Другой пример — бесконечная последовательность. Генератор может генерировать значения без ограничения, например, числа Фибоначчи:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
for _ in range(10):
print(next(fib)) # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34Подводные камни
- Генератор одноразовый: после исчерпания (когда функция завершилась) его нельзя перезапустить. Для повторного использования нужно создать новый генератор.
- Генератор не поддерживает индексацию и срезы — только последовательный доступ через
next()или цикл. - Если генератор не исчерпан, он может держать ресурсы (например, открытый файл). Закрыть его можно методом
close(), но обычно это не требуется, если генератор используется вwithили завершается естественно. - При передаче генератора в функцию, которая ожидает список (например,
len()), нужно явно преобразовать его в список:list(gen). Это может свести на нет экономию памяти. - Исключение
StopIterationвозникает, когда генератор исчерпан; в циклеforоно обрабатывается автоматически.
Когда использовать
Генераторы стоит применять, когда:
- обрабатываются большие или бесконечные последовательности данных (файлы, потоки, логи);
- нужно организовать конвейер обработки, где каждый этап передаёт данные следующему без промежуточного хранения;
- требуется ленивое вычисление, чтобы отложить дорогостоящие операции до момента фактического использования.
Не стоит использовать генераторы, если нужен многократный доступ к элементам по индексу или случайный доступ — для этого лучше подойдёт список.
Коротко
- Генератор создаётся функцией с
yieldили генераторным выражением; он отдаёт значения по одному, сохраняя состояние между вызовами. - Главное преимущество — экономия памяти и ленивая обработка данных.
- Генератор одноразовый и не поддерживает индексацию; для повторного использования создавайте новый.
- Используйте генераторы для больших данных, бесконечных последовательностей и конвейерной обработки.
