← Назад к вопросам
Как получить значение в Python генераторе, посылаемое через метод send()?
2.0 Middle🔥 101 комментариев
#Python Core#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Получение значений через send() в генераторах Python
Метод send() позволяет отправить значение в работающий генератор и получить результат его выполнения. Это мощный инструмент для двусторонней коммуникации.
Базовый механизм
Как работает send()
def simple_generator():
# Ожидаем первого значения
x = yield "First pause"
print(f"Получили значение: {x}")
# Ожидаем второго значения
y = yield "Second pause"
print(f"Получили второе значение: {y}")
return "Generator finished"
# Создаём генератор
gen = simple_generator()
# Первый вызов next() или send(None)
# Выполняется до первого yield
result1 = next(gen)
print(f"Результат 1: {result1}") # Выведет: "First pause"
# Отправляем значение 42 в генератор
# Это становится результатом выражения x = yield "First pause"
result2 = gen.send(42)
print(f"Получили значение: 42") # От print в генераторе
print(f"Результат 2: {result2}") # Выведет: "Second pause"
# Отправляем второе значение
result3 = gen.send(100)
print(f"Получили второе значение: 100")
# Генератор завершился
Вывод:
Результат 1: First pause
Получили значение: 42
Результат 2: Second pause
Получили второе значение: 100
Важное уточнение: первый вызов
def example():
value = yield "Start"
print(f"Value: {value}")
gen = example()
# ❌ ОШИБКА - не можно отправить значение до первого yield
try:
gen.send(42) # TypeError!
except TypeError as e:
print(f"Ошибка: {e}")
# TypeError: can't send non-None value to a just-started generator
# ✅ ПРАВИЛЬНО - сначала next() или send(None)
gen = example()
result = gen.send(None) # Или next(gen)
print(f"Result: {result}") # "Start"
# Теперь отправляем
gen.send(42)
Практические примеры
1. Двусторонний обмен данными
def echo_generator():
"""Генератор эхо - возвращает то, что ему отправляют"""
while True:
x = yield
print(f"Эхо: {x}")
gen = echo_generator()
next(gen) # Инициализируем
gen.send("Hello")
gen.send("World")
gen.send(123)
# Вывод:
# Эхо: Hello
# Эхо: World
# Эхо: 123
2. Потребитель-производитель
def producer():
"""Производитель - отправляет данные потребителю"""
for i in range(1, 6):
result = yield i
print(f"Потребитель обработал: {result}")
def consumer():
"""Потребитель - обрабатывает данные"""
consumed = 0
while True:
data = yield f"Обработано {consumed} элементов"
if data:
consumed += 1
print(f"Потребитель получил: {data}")
# Использование
prod = producer()
cons = consumer()
# Инициализируем оба
print(next(prod))
print(next(cons))
# Производитель отправляет данные потребителю
for value in [1, 2, 3, 4, 5]:
print(f"\nПроизводитель отправляет: {value}")
result = prod.send(value)
print(f"Потребитель ответит: {result}")
result = cons.send(value)
3. Асинхронный callback паттерн
def fetch_user_data(user_id: int):
"""Генератор для асинхронного получения данных"""
print(f"Запрашиваю данные пользователя {user_id}")
# yield останавливает генератор и возвращает запрос
response = yield f"GET /users/{user_id}"
# После send() получаем ответ
user_data = response
print(f"Получены данные: {user_data}")
# Обрабатываем данные
return {"user": user_data, "processed": True}
# Симуляция фреймворка (как asyncio)
def run_generator(gen):
"""Симуляция event loop"""
request = None
while True:
try:
if request is None:
request = next(gen)
else:
# request - это URL который нужно получить
# Симулируем HTTP запрос
response = {"id": 1, "name": "Alice", "email": "alice@example.com"}
request = gen.send(response)
except StopIteration as e:
return e.value
# Запуск
gen = fetch_user_data(1)
result = run_generator(gen)
print(f"Результат: {result}")
# Вывод:
# Запрашиваю данные пользователя 1
# Получены данные: {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}
# Результат: {'user': {...}, 'processed': True}
Обработка ошибок с throw()
def error_handler():
try:
x = yield "Waiting"
print(f"Получили: {x}")
y = yield "Waiting again"
print(f"Получили: {y}")
except ValueError as e:
print(f"Поймана ошибка: {e}")
yield "Error handled"
gen = error_handler()
print(next(gen)) # "Waiting"
print(gen.send(42)) # "Waiting again"
# Отправляем исключение
print(gen.throw(ValueError("Что-то пошло не так")))
# Выведет:
# Поймана ошибка: Что-то пошло не так
# Error handled
Связь yield и return в генераторе
def generator_with_return():
yield 1
yield 2
return "Done" # Значение попадает в StopIteration.value
gen = generator_with_return()
print(next(gen)) # 1
print(next(gen)) # 2
try:
next(gen)
except StopIteration as e:
print(f"Возвращённое значение: {e.value}") # "Done"
# Или с send()
gen = generator_with_return()
print(gen.send(None)) # 1
print(gen.send(None)) # 2
try:
gen.send(None)
except StopIteration as e:
print(f"Возвращённое значение: {e.value}")
Корутины и двусторонняя коммуникация
def coroutine_sum():
"""Корутина - складывает отправляемые значения"""
total = 0
while True:
x = yield total # Отправляем текущее значение
if x is None:
break
total += x
return total
# Использование
gen = coroutine_sum()
print(gen.send(None)) # 0 (инициализация)
print(gen.send(10)) # 10
print(gen.send(20)) # 30
print(gen.send(5)) # 35
try:
gen.send(None) # Выход из цикла
except StopIteration as e:
print(f"Сумма: {e.value}") # 35
Практическое применение
Пайпайн обработки данных
def filter_even():
"""Фильтр - пропускает только чётные числа"""
while True:
x = yield
if x and x % 2 == 0:
print(f"Чётное: {x}")
def double():
"""Трансформация - удваивает числа"""
while True:
x = yield
if x:
print(f"Удвоенное: {x * 2}")
# Создание пайпайна
filter_gen = filter_even()
double_gen = double()
next(filter_gen)
next(double_gen)
# Отправляем данные
for num in range(1, 6):
print(f"\nОтправляем: {num}")
filter_gen.send(num)
double_gen.send(num)
# Вывод:
# Отправляем: 1
# Отправляем: 2
# Чётное: 2
# Удвоенное: 4
# ...
Жизненный цикл генератора с send()
1. gen = generator() - создание
2. next(gen) или gen.send(None) - инициализация, выполнение до первого yield
3. gen.send(value) - отправка значения, выполнение до следующего yield
4. gen.throw(exc) - отправка исключения
5. gen.close() - закрытие генератора
Когда использовать send()
- Корутины - двусторонний обмен данными
- Пайпайны - обработка потоков данных
- Фреймворки - asyncio использует send() для управления
- Симуляции - event-driven системы
Современная альтернатива: async/await
В Python 3.5+ вместо генераторов с send() используют async/await:
async def fetch_data(user_id):
response = await fetch_user(user_id) # Вместо yield
return response
Это выглядит понятнее, но на уровне CPython использует тот же механизм.
Итоговый пример: Состояние машины
def state_machine():
state = "idle"
while True:
event = yield state
if state == "idle" and event == "start":
state = "running"
elif state == "running" and event == "pause":
state = "paused"
elif state == "paused" and event == "resume":
state = "running"
elif state == "running" and event == "stop":
state = "idle"
fsm = state_machine()
print(next(fsm)) # "idle"
print(fsm.send("start")) # "running"
print(fsm.send("pause")) # "paused"
print(fsm.send("resume")) # "running"
print(fsm.send("stop")) # "idle"
Метод send() - это ключ к пониманию Python корутин и асинхронного программирования!