Генераторы в Python - это специальный тип итераторов, который позволяет создавать последовательности значений "на лету", без необходимости хранить все значения в памяти одновременно. Генераторы используют ключевое слово yield вместо return и сохраняют свое состояние между вызовами.
Генераторы можно создавать двумя способами: с помощью функций с yield и с помощью генераторных выражений:
# Простой генератор чисел от 1 до n
def простой_генератор(n):
for i in range(1, n + 1):
yield i
# Использование генератора
генератор = простой_генератор(5)
print("Значения из генератора:")
for значение in генератор:
print(значение)
# Генератор, который возвращает только четные числа
def четные_числа(начало, конец):
for число in range(начало, конец + 1):
if число % 2 == 0:
yield число
# Использование генератора четных чисел
print("\nЧетные числа от 1 до 20:")
for четное in четные_числа(1, 20):
print(четное, end=" ")
print()
# Генератор последовательности Фибоначчи
def фибоначчи(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
# Использование генератора Фибоначчи
print("\nПервые 10 чисел Фибоначчи:")
for число in фибоначчи(10):
print(число, end=" ")
print()
Генераторные выражения похожи на списковые включения, но возвращают генератор вместо списка:
# Генераторное выражение для квадратов чисел
квадраты = (x**2 for x in range(1, 11))
print("Квадраты чисел от 1 до 10:")
for квадрат in квадраты:
print(квадрат, end=" ")
print()
# Сравнение с списковым включением
# Списковое включение - создает список сразу
список_квадратов = [x**2 for x in range(1, 11)]
print(f"Список квадратов: {список_квадратов}")
# Генераторное выражение - создает генератор
генератор_квадратов = (x**2 for x in range(1, 11))
print(f"Генератор квадратов: {генератор_квадратов}")
print(f"Первое значение: {next(генератор_квадратов)}")
# Генераторное выражение с фильтрацией
четные_квадраты = (x**2 for x in range(1, 21) if x % 2 == 0)
print("\nКвадраты четных чисел от 1 до 20:")
for квадрат in четные_квадраты:
print(квадрат, end=" ")
print()
Генераторы имеют несколько полезных методов для управления их поведением:
# Пример генератора с возможностью отправки значений
def генератор_с_отправкой():
значение = 0
while True:
# Получаем значение, отправленное через send()
отправленное = yield значение
if отправленное is not None:
значение = отправленное
else:
значение += 1
# Использование метода send()
ген = генератор_с_отправкой()
print("Начальное значение:", next(ген)) # 0
print("Следующее значение:", next(ген)) # 1
print("Отправляем значение 10:", ген.send(10)) # 10
print("Следующее значение:", next(ген)) # 11
# Генератор с обработкой исключений
def генератор_с_исключениями():
try:
yield 1
yield 2
yield 3
except ValueError as e:
print(f"Поймано исключение: {e}")
yield "Ошибка"
# Использование метода throw()
ген2 = генератор_с_исключениями()
print("\nЗначения из генератора с исключениями:")
print(next(ген2)) # 1
print(next(ген2)) # 2
# Отправляем исключение в генератор
print(ген2.throw(ValueError, "Тестовое исключение")) # Ошибка
# Генератор с возможностью завершения
def генератор_с_завершением():
try:
yield 1
yield 2
yield 3
except GeneratorExit:
print("Генератор завершается")
raise
# Использование метода close()
ген3 = генератор_с_завершением()
print("\nЗначения из генератора с завершением:")
print(next(ген3)) # 1
# Завершаем генератор
ген3.close()
Генераторы особенно полезны при работе с большими объемами данных или при создании бесконечных последовательностей:
# Создание примера большого файла
with open("большой_файл.txt", "w", encoding="utf-8") as файл:
for i in range(1000):
файл.write(f"Строка номер {i+1}\n")
# Генератор для чтения файла построчно
def читать_файл_построчно(имя_файла):
with open(имя_файла, "r", encoding="utf-8") as файл:
for строка in файл:
yield строка.strip()
# Использование генератора для обработки большого файла
print("Первые 10 строк файла:")
счетчик = 0
for строка in читать_файл_построчно("большой_файл.txt"):
if счетчик >= 10:
break
print(строка)
счетчик += 1
# Генератор для фильтрации строк
def фильтровать_строки(имя_файла, ключевое_слово):
with open(имя_файла, "r", encoding="utf-8") as файл:
for строка in файл:
if ключевое_слово in строка:
yield строка.strip()
# Использование фильтрующего генератора
print("\nСтроки, содержащие '500':")
for строка in фильтровать_строки("большой_файл.txt", "500"):
print(строка)
# Генератор бесконечной последовательности простых чисел
def простые_числа():
def является_простым(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
число = 2
while True:
if является_простым(число):
yield число
число += 1
# Использование генератора простых чисел
print("Первые 20 простых чисел:")
простые = простые_числа()
for _ in range(20):
print(next(простые), end=" ")
print()
# Генератор случайных чисел
import random
def случайные_числа(минимум, максимум):
while True:
yield random.randint(минимум, максимум)
# Использование генератора случайных чисел
print("\n10 случайных чисел от 1 до 100:")
случайные = случайные_числа(1, 100)
for _ in range(10):
print(next(случайные), end=" ")
print()
Сравнение производительности генераторов и списков:
import sys
import time
# Создание большого списка
список = [x for x in range(1000000)]
print(f"Размер списка: {sys.getsizeof(список)} байт")
# Создание генератора
генератор = (x for x in range(1000000))
print(f"Размер генератора: {sys.getsizeof(генератор)} байт")
# Измерение времени создания
# Время создания списка
начало = time.time()
список2 = [x for x in range(1000000)]
конец = time.time()
print(f"Время создания списка: {конец - начало:.4f} секунд")
# Время создания генератора
начало = time.time()
генератор2 = (x for x in range(1000000))
конец = time.time()
print(f"Время создания генератора: {конец - начало:.4f} секунд")
# Итерация по списку
начало = time.time()
сумма_списка = sum(список2)
конец = time.time()
print(f"Время итерации по списку: {конец - начало:.4f} секунд")
# Итерация по генератору
начало = time.time()
сумма_генератора = sum(генератор2)
конец = time.time()
print(f"Время итерации по генератору: {конец - начало:.4f} секунд")
Рассмотрим комплексные примеры использования генераторов:
# Генератор для обработки данных о продажах
def обработать_продажи(данные_о_продажах):
for запись in данные_о_продажах:
# Предположим, что запись - это словарь с данными о продаже
if запись["сумма"] > 1000: # Только крупные продажи
yield {
"id": запись["id"],
"сумма": запись["сумма"],
"бонус": запись["сумма"] * 0.05 # 5% бонус
}
# Пример данных о продажах
продажи = [
{"id": 1, "сумма": 500},
{"id": 2, "сумма": 1500},
{"id": 3, "сумма": 800},
{"id": 4, "сумма": 2500},
{"id": 5, "сумма": 1200}
]
# Использование генератора
print("Крупные продажи с бонусами:")
for продажа in обработать_продажи(продажи):
print(f"Продажа #{продажа['id']}: {продажа['сумма']} руб., бонус: {продажа['бонус']:.2f} руб.")
# Генератор для создания отчетов
def создать_отчет(данные, тип_отчета):
if тип_отчета == "ежедневный":
for запись in данные:
yield f"Ежедневный отчет: {запись}"
elif тип_отчета == "ежемесячный":
for запись in данные:
yield f"Ежемесячный отчет: {запись}"
else:
yield "Неизвестный тип отчета"
# Пример использования
данные = ["Продажи", "Заказы", "Клиенты"]
print("\nЕжедневные отчеты:")
for отчет in создать_отчет(данные, "ежедневный"):
print(отчет)
Задание: Создайте генератор чисел Фибоначчи, который останавливается, когда значение превышает заданный предел.
Требования:
def фибоначчи_до_предела(предел):
a, b = 0, 1
while a <= предел:
yield a
a, b = b, a + b
# Тестирование
print("Числа Фибоначчи до 100:")
for число in фибоначчи_до_предела(100):
print(число, end=" ")
print()
Задание: Создайте генератор, который возвращает только уникальные значения из последовательности, сохраняя порядок их первого появления.
Требования:
def уникальные_значения(последовательность):
встреченные = []
for значение in последовательность:
if значение not in встреченные:
встреченные.append(значение)
yield значение
# Тестирование
данные = [1, 2, 2, 3, 1, 4, 3, 5]
print("Уникальные значения:")
for значение in уникальные_значения(данные):
print(значение, end=" ")
print()
Задание: Создайте генератор, который читает файл логов и возвращает только строки с ошибками (содержащие слово "ERROR").
Требования:
def ошибки_в_логах(имя_файла):
try:
with open(имя_файла, "r", encoding="utf-8") as файл:
for строка in файл:
if "ERROR" in строка:
yield строка.strip()
except FileNotFoundError:
print(f"Файл {имя_файла} не найден")
except Exception as e:
print(f"Ошибка при чтении файла: {e}")
# Создание примера лог файла
пример_лога = """INFO: Приложение запущено
ERROR: Ошибка подключения к базе данных
INFO: Пользователь вошел в систему
ERROR: Неверный пароль
WARNING: Низкий уровень памяти
INFO: Операция выполнена успешно
ERROR: Неизвестная ошибка"""
with open("app.log", "w", encoding="utf-8") as файл:
файл.write(пример_лога)
# Тестирование
print("Ошибки в логах:")
for ошибка in ошибки_в_логах("app.log"):
print(ошибка)