После приостановки внешнего генератора на yield from обычного списка ему передали ненулевое значение через ...

После приостановки внешнего генератора на yield from обычного списка ему передали ненулевое значение через send(). Что произойдёт с этим значением?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Значение будет проигнорировано, а не передано элементам списка. Делегирование продолжит получать следующий элемент у итератора списка, поэтому внешний генератор выдаст его потребителю.

Исторический контекст

Конструкция yield from появилась в Python 3.3 для удобного делегирования генератором другой итерации. Она устраняет ручной код для передачи значений, обработки завершения и взаимодействия с вложенным генератором.

При этом делегироваться может не только генератор, но и любой итерируемый объект. Обычный список предоставляет итератор без метода send, поэтому он не способен обработать отправленное значение как управляющий сигнал.

Постановка проблемы

Внешний генератор может выглядеть как единый поток данных, хотя фактически часть элементов получает от вложенного объекта. Если потребитель вызывает send(), важно понимать, поддерживает ли делегируемый объект приём таких значений.

Ошибочное ожидание обработки сигнала списком может привести к потере управляющей информации. При этом исключения не будет: ненулевое значение молча отбрасывается, если у текущего делегируемого итератора отсутствует метод send.

Подробное решение

Когда внешний генератор приостановлен внутри yield from, вызов send(value) маршрутизируется к делегируемому объекту. Если у него есть метод send, вызывается именно этот метод.

Если метода send нет, Python не пытается вызвать его искусственно. Для ненулевого значения он просто продолжает итерацию обычным вызовом next(), а переданное значение отбрасывает. Поэтому список или его итератор не может получить такое значение.

def relay(): yield from [10, 20] generator = relay() print(next(generator)) print(generator.send("сигнал"))

Сначала будет выведено 10, затем 20. Строка передана внешнему генератору, но итератор списка её не обработал.

Это поведение отличается от делегирования вложенному генератору: вложенный генератор обычно реализует send и может использовать полученное значение в выражении, приостановленном на yield. Поэтому при проектировании протокола обмена сигналами нужно явно выбирать делегируемый объект, способный эти сигналы принимать.

Ситуация из практики

Сервис строит поток обработки: внешний генератор делегирует элементы коллекции, а потребитель пытается отправлять команды управления через send(). Если источником является список, команды не повлияют на обработку и будут потеряны.

Можно оставить список и рассчитывать только на последовательное получение элементов. Это просто и эффективно по памяти при обходе, но не поддерживает обратную связь.

Другой вариант — обернуть источник во вложенный генератор, который явно обрабатывает значения, поступающие через send. Такой подход сложнее, зато контракт управления становится явным и расширяемым.

Практически выбирают вложенный генератор, если обратная связь является частью протокола. Если же нужны только данные без управляющих сигналов, список или его итератор остаётся более простым решением.

Что кандидаты часто упускают

  1. Что произойдёт, если делегируемый объект сам поддерживает send?

    Ненулевое значение будет передано его методу send. Результат этого вызова станет следующим значением, которое получит потребитель внешнего генератора. Таким образом, yield from прозрачно проксирует обратную связь вложенному генератору.

  2. Чем отличается send(None) при делегировании?

    Значение None рассматривается как обычное продолжение итерации. Python вызывает next() у делегируемого объекта, независимо от наличия у него метода send. Это соответствует смыслу возобновления генератора без передачи нового значения.

  3. Можно ли заставить список обработать отправленное значение через yield from?

    Нет. Итератор списка не определяет протокол приёма значений и не имеет состояния, связанного с send. Для обработки обратной связи нужен объект-делегат с соответствующей логикой, обычно вложенный генератор или пользовательский итератор с методом send.