Программирование • 18 марта 2025 • 5 мин чтения

Генераторы Python: что это такое и зачем они нужны

Генераторы в Python создают последовательности данных. Они не занимают много памяти, а код с ними работает быстрее. Рассказываем, как их создавать и использовать.

Что такое генератор в Python и для чего он нужен

Если программист работает с большими объёмами данных, важно не загружать всю информацию в память, а делать это постепенно. Для этого в Python используются генераторы — специальные объекты, которые по требованию генерируют элементы последовательности.

Обычная функция в Python завершает работу после return и возвращает результат. Генератор работает иначе. Вместо return он использует yield, который приостанавливает выполнение, но сохраняет текущее состояние. При следующем вызове выполнение продолжается с того же момента, где оно было остановлено.

Генераторы в Python используются для решения нескольких задач:

Снижение нагрузки на память. Генераторы помогают обрабатывать большие объёмы данных, например считывать файлы построчно без загрузки в оперативную память.
Работа с бесконечными последовательностями. Генераторы создают потоки данных, например случайные числа или значения в цикле без предела.
Оптимизация кода. Они делают код компактнее и удобнее: не нужно использовать дополнительные структуры для хранения промежуточных данных.

Генераторы облегчают работу Python-разработчика

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

Артём Стрельцов, разработчик Генераторы можно использовать для контекстных менеджеров, чтобы автоматически управлять ресурсами. Код до yield выполняется при входе в контекст (__enter__).
Код после yield выполняется при выходе (__exit__). В Python есть contextlib.contextmanager, который превращает функцию с yield в контекстный менеджер.

Освоить бэкенд-разработку с нуля всего за 10 месяцев можно на курсе «Python-разработчик». Студенты изучают алгоритмы и структуры данных, API, асинхронность, а навыки отрабатывают на специальном онлайн-тренажёре.

Создание генератора в Python

В Python генератор можно создать двумя способами:

1. С помощью функции с yield.
2. С помощью генераторного выражения.

Создание генератора через yield

Генератор — это функция, которая возвращает значения по очереди с помощью yield, а не завершает работу, как при return. Она запоминает, где остановилась, и при следующем вызове продолжает выполнение с того же места. Например, генератор ниже создаёт последовательность чисел:

def simple_generator():
yield 1
yield 2
yield 3

gen = simple_generator()
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3

Попытка вызвать next(gen) ещё раз приведёт к ошибке StopIteration, так как элементы закончились.

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

Генератор можно создать в одну строку с помощью генераторного выражения. Этот способ аналогичен списковым включениям, но вместо [ ] используют ( ). Вот как может выглядеть код:

gen_exp = (x ** 2 for x in range(5))

print(next(gen_exp)) # 0
print(next(gen_exp)) # 1
print(next(gen_exp)) # 4

Дальше расскажем про генераторные выражения подробнее.

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

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

Генераторное выражение выглядит как списковое включение, но использует круглые скобки ( ) вместо квадратных [ ]:

(expression for item in iterable if condition)

expression — что делать с каждым элементом;
item — переменная для текущего элемента;
iterable — последовательность (например, список, кортеж, диапазон);
if condition (необязательно) — фильтр для выбора элементов.

Допустим, нужно создать последовательность квадратов чисел от 0 до 9. Значения будут вычисляться по мере необходимости, а не загружаться в память сразу:

gen = (x ** 2 for x in range(10))

print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 4


Списковое включение — создаёт весь список сразу


Генераторное выражение — создаёт по одному значению

squares_list = [x ** 2 for x in range(10)]

squares_gen = (x ** 2 for x in range(10))

Работа с генераторами

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


Итерация по генератору. Генераторы можно перебирать в цикле for, при этом ошибки StopIteration не будет, так как цикл сам завершится


def count_down(n):
while n > 0:
yield n
n -= 1

for num in count_down(3):
print(num)

Вывод:
3
2
1
Значения генерируются по одному

Использование next ( ). Можно вручную получать значения из генератора с помощью next ( )

gen = (x for x in range(3))

print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # Ошибка StopIteration!

Чтобы избежать ошибки StopIteration, можно использовать next() с аргументом по умолчанию:

gen = (x for x in range(3))

print(next(gen, "Конец")) # 0
print(next(gen, "Конец")) # 1
print(next(gen, "Конец")) # 2
print(next(gen, "Конец")) # Конец

Прерывание генератора: return. В генераторной функции можно использовать return, чтобы остановить генератор

def my_gen():
yield 1
yield 2
return # Генератор завершится тут
yield 3 # Это никогда не выполнится

gen = my_gen()
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # StopIteration

Бесконечный генератор. Генератор может работать бесконечно, пока его не остановить. Но такой генератор нельзя перебирать for, иначе программа зациклится

def infinite_counter():
n = 1
while True:
yield n
n += 1

counter = infinite_counter()
print(next(counter)) # 1
print(next(counter)) # 2
print(next(counter)) # 3

Генератор с send(value) позволяет отправлять данные в генератор

def my_gen():
value = yield "Начало"
yield f"Получено: {value}"

gen = my_gen()
print(next(gen)) # "Начало"
print(gen.send("Hello")) # "Получено: Hello"

Генератор с throw(exception) позволяет вызвать исключение внутри генератора

def my_gen():
try:
yield "Ожидание..."
except ValueError:
yield "Ошибка!"

gen = my_gen()
print(next(gen)) # "Ожидание..."
print(gen.throw(ValueError)) # "Ошибка!"

Генератор с close ( ) закрывает генератор, вызывает GeneratorExit

def my_gen():
try:
yield "Работаю..."
except GeneratorExit:
print("Генератор закрыт!")

gen = my_gen()
print(next(gen)) # "Работаю..."
gen.close() # Генератор закрывается

Практическое применение генераторов в Python

Генераторы в Python особенно полезны, когда работа идёт с большими объёмами данных, потоками информации или бесконечными последовательностями. Рассмотрим реальные примеры, где генераторы могут быть полезны.

Чтение больших файлов построчно. Если файл очень большой, загружать его целиком в память неэффективно. Генератор позволяет читать файл построчно:

def read_large_file(filename):
with open(filename, "r", encoding="utf-8") as file:
for line in file:
yield line.strip() # Убираем лишние пробелы

# Использование:
for line in read_large_file("huge_data.txt"):
print(line) # Читаем строки по одной

Бесконечные последовательности — например, ID пользователей. Генератор позволяет создавать неограниченные последовательности и не хранить их в памяти.

def unique_id_generator():
id = 1
while True:
yield id
id += 1

id_gen = unique_id_generator()

print(next(id_gen)) # 1
print(next(id_gen)) # 2
print(next(id_gen)) # 3

Генерация случайных данных. Генератор можно использовать для создания случайных паролей:

import random
import string

def random_password(length=8):
while True:
yield ''.join(random.choices(string.ascii_letters + string.digits, k=length))

password_gen = random_password()

print(next(password_gen)) # Генерирует случайный пароль
print(next(password_gen))

Постепенная загрузка данных из API. При работе с API лучше загружать данные постепенно, а не всю информацию сразу:

import requests

def fetch_data(url):
response = requests.get(url, stream=True)
for line in response.iter_lines():
if line:
yield line.decode('utf-8')

# Использование:
for data in fetch_data("https://example.com/large-data"):
print(data) # Читаем построчно

Совет эксперта

Артём Стрельцов
Генераторы — это не базовый уровень знаний Python. Но их использование может существенно упростить и оптимизировать код. Этот инструмент имеет свои особенности, поэтому использовать его стоит с пониманием.
Статью подготовили:
Артём Стрельцов
Яндекс Практикум
Разработчик
Надежда Низамова
Яндекс Практикум
Редактор
Анастасия Павлова
Яндекс Практикум
Иллюстратор

Дайджест блога: ежемесячная подборка лучших статей от редакции

Поделиться
Угадайте, где правда, а где фейк про IT, и получите скидку на курсы Практикума
Thu Mar 20 2025 14:57:30 GMT+0300 (Moscow Standard Time)