Назначение функции 'send' в генераторах Python: примеры использования
Пройдите тест, узнайте какой профессии подходите
Быстрый ответ
Основная функция метода send()
заключается в передаче значений в генератор на стадии yield
, чем обеспечивается возможность регулирования их поведения в процессе выполнения. Это отличает его от метода next()
, который лишь продвигает генератор к следующему значению yield
.
Посмотрим пример:
def countdown(start):
current = start
while current > 0:
received = (yield current)
current = received if received is not None else current – 1
gen = countdown(5)
print(next(gen)) # напечатает 5. Запускаем обратный отсчёт.
print(gen.send(3)) # напечатает 3. Это нечто вроде фокуса!
print(next(gen)) # напечатает 2. Продолжаем обратный отсчёт.
Из этого примера становится ясно, что метод gen.send(3)
перезапускает отсчёт со значения 3. Таким образом демонстрируется динамическое управление, к которому позволяет прийти send()
.
Применение send
для упрощения асинхронного кода
В контексте асинхронных операций, в частности с использованием фреймворков типа Twisted, метод send
может превратить код в более читаемый и менее запутанный. С применением декоратора @defer.inlineCallbacks
и метода send
, есть возможность управлять "определёнными" значениями (Deferred values), избегая при этом сложностей, связанных с callback'ами. Вот так выглядит пример, в котором send
упрощает структуру асинхронного кода и дает возможность корутинам сгенерировать и получить результаты на лету.
@defer.inlineCallbacks
def async_operation():
result = yield some_deferred_operation()
print('Операция выполнена:', result)
new_result = yield another_deferred_operation(result)
defer.returnValue(new_result)
в этом случае yield
внутри @defer.inlineCallbacks
автоматически обрабатывается с Deferreds, что делает возможным написание более понятного и линейного кода, упрощая тем самым понимание асинхронных операций.
send
: Двухсторонний канал связи
Метод send()
создаёт двусторонний канал связи между генераторами. Эта двухсторонняя связь делает генераторы корутинами, оптимизированными для асинхронных операций ввода-вывода.
def user_input_processor():
result = ""
while True:
user_input = (yield result)
result = f"Обработано: {user_input}"
processor = user_input_processor()
next(processor) # Инициализация процессора
print(processor.send("Привет")) # напечатает: "Обработано: Привет"
print(processor.send("Мир!")) # напечатает: "Обработано: Мир!"
Отсюда видно, как send
реально меняет поведение генератора на основе введённых данных, делая генераторы отзывчивыми и адаптируемыми.
Изменение поведения в реальном времени с помощью send
Метод send()
даёт возможность изменять поведение генератора в реальном времени путём передачи новых значений. Это делает генераторы гибкими и быстро реагирующими на изменения вызывающего кода, как видно из нижеприведённого примера кода:
def traffic_light():
color = 'Зелёный'
while True:
color = (yield color)
color = 'Красный' if color == 'Жёлтый' else 'Зелёный'
light = traffic_light()
print(next(light)) # напечатает 'Зелёный'
print(light.send('Жёлтый')) # напечатает 'Красный'. Стоп!
Визуализация
Проиллюстрируем взаимодействие с генератором Python используя функцию send:
Представим игровой контроллер (🎮), который управляет персонажем (🧍):
def game_character():
action = yield "Жду команд от игрока..."
while True:
if action == "MOVE":
yield "Персонаж идёт!"
elif action == "JUMP":
yield "Персонаж прыгает!"
else:
yield "Персонаж не понимает действие..."
Каждое нажатие кнопки на 🎮 приводит к выполнению команды для персонажа 🧍:
🎮 `.send("JUMP")`: Персонаж прыгает! Вот что значит прыгун!
🎮 `.send("MOVE")`: Персонаж идёт! Начинаем путь, пока играет мелодия.
🎮 `.send(None)`: Персонаж не понимает действие... Где инструкция?
Действия персонажа контролируются через send()
, что обеспечивает присутствие интерактивности в игре.
Поддержка состояния с помощью "send"
Генераторы, использующие send
, способны хранить своё состояние между операторами yield
на протяжении всего своего жизненного цикла. Эта особенность позволяет реализовать такие варианты использования, как подсчёт промежуточной суммы или машины состояний, что непостижимо для традиционных методов:
def running_total():
total = 0
while True:
addend = (yield total)
total += addend if addend is not None else 0
accumulator = running_total()
next(accumulator) # готовим аккумулятор
print(accumulator.send(5)) # прибавляем 5; напечатает 5
print(accumulator.send(10)) # прибавляем 10; напечатает 15
Здесь через send()
к ранее вычисленной сумме аккумулятора добавляются новые значения.
Интерактивные шаблоны с использованием “send”
Сочетание методов send()
и yield
позволяет создать интерактивные шаблоны генераторов. Это уже не просто пассивные производители последовательностей. Генераторы могут динамически отвечать на внешние команды:
def interactive_quiz():
score = 0
question = (yield f"Счёт: {score}. Давайте начнём: сколько будет два плюс два?")
while True:
if question == "4":
score += 1
question = (yield f"Отлично! Счёт: {score}. Следующий вопрос: какой город является столицей Франции?")
else:
question = (yield f"Неверно. Счёт: {score}. Попробуйте ещё раз!")
quizinator = interactive_quiz()
next(quizinator) # Задаём первый вопрос
print(quizinator.send("4")) # Последует следующий вопрос
Здесь генератор функционирует как простейшая интерактивная викторина, обладающая способностью к самостоятельному взаимодействию.
Полезные материалы
- PEP 342 – Реализация сопрограмм с помощью расширенных генераторов — ключевое предложение, которое описывает, как
send
расширяет возможности генераторов в Python. - 6. Выражения — Официальная документация Python 3 — официальная документация Python, где подробно описаны
yield
выражения в контексте генераторов. - Сопрограммы и задачи — Официальная документация Python 3 — подробности о сопрограммах asyncio и связанных с ними задачах для параллельного выполнения кода.
- Генераторы – Python Wiki — обзор возможностей генераторов с выделением описания метода
send
. - Эффективный Python: 90 специфических рекомендаций для написания более качественного кода на Python — советы касательно эффективного использования итераторов и генераторов для улучшения кода на Python.