Генераторы (Generators) в Python
Генераторы (Generators) в Python
Определение
Генератор — это функция, которая возвращает последовательность значений лениво (по требованию). Генераторы создаются с помощью ключевого слова yield и позволяют сохранять состояние между вызовами. Они не хранят все элементы в памяти, что эффективно для работы с большими данными.
Ключевое слово yield
Когда функция содержит yield, она автоматически становится генератором. При вызове next() выполнение функции приостанавливается на yield, а при следующем вызове возобновляется с того же места.
def simple_generator():
yield 1
yield 2
yield 3
gen = simple_generator()
print(next(gen)) # 1
print(next(gen)) # 2
Генераторные выражения
Аналогичны списковым включениям, но используют круглые скобки () и возвращают элементы по одному.
# Генераторное выражение
gen_expr = (x**2 for x in range(5))
print(next(gen_expr)) # 0 (элементы генерируются на лету)
Сравнение с включениями (Comprehensions)
| Критерий | Списковое включение | Генератор |
|---|---|---|
| Память | Хранит все элементы в памяти | Генерирует элементы на лету |
| Использование | Для небольших данных | Для больших/бесконечных данных |
| Пример | [x**2 for x in range(10)] | (x**2 for x in range(10)) |
Методы генераторов
1. send(value)
Передает значение в генератор (становится результатом yield).
def responsive_gen():
while True:
received = yield
print(f"Received: {received}")
gen = responsive_gen()
next(gen) # Запускает генератор
gen.send("Hello") # Received: Hello
2. throw(type)
Вызывает исключение внутри генератора.
def error_gen():
try:
yield 1
except ValueError:
yield "Error"
gen = error_gen()
next(gen) # 1
print(gen.throw(ValueError)) # Error
3. close()
Останавливает генератор.
gen = simple_generator()
gen.close()
next(gen) # StopIteration
Корутины (Coroutines)
Генераторы с send() могут использоваться как корутины — функции, обменивающиеся данными с вызывающим кодом. Это основа асинхронного программирования в Python (до появления async/await).
def coroutine():
while True:
x = yield
print(f"Processing: {x}")
coro = coroutine()
next(coro) # Инициализация
coro.send(10) # Processing: 10
Копирование генераторов
Генераторы нельзя скопировать стандартными методами (deepcopy, copy, срезы), так как они хранят состояние выполнения.
Примеры ошибок:
import copy
gen = simple_generator()
# copy.copy(gen) # Ошибка: генераторы не поддерживают копирование
# gen_slice = gen[1:] # Ошибка: генераторы не поддерживают срезы
Решение:
- Пересоздайте генератор.
- Используйте
itertools.tee()для разделения итератора на несколько (но это работает только для некоторых итераторов).
import itertools
gen = simple_generator()
gen1, gen2 = itertools.tee(gen, 2)
print(next(gen1)) # 1
print(next(gen2)) # 1 (но исходный gen будет истощен)
Заключение
- Генераторы идеальны для работы с большими данными или бесконечными последовательностями.
- Генераторные выражения экономят память, заменяя списковые включения.
- Методы
send(),throw(),close()расширяют возможности генераторов, превращая их в корутины. - Корутины позволяют реализовать кооперативную многозадачность.
- Копирование генераторов невозможно из-за их состояния — используйте пересоздание или
itertools.tee().
Совет: Для сложных сценариев (например, асинхронности) используйте современные конструкции asyncio и async/await, которые развивают идеи генераторов.