Разница между `yield from foo()` и `for x в foo(): yield x`

В Python большинство примеров урожая объясняют это тем, что

yield from foo()

похож на

for x in foo(): yield x

С другой стороны, это не похоже на одно и то же, и в нем есть какая-то магия. Мне немного неловко использовать функцию, которая делает магию, которую я не понимаю. Что мне нужно знать о магии yield from, чтобы избежать попадания в ситуацию, когда магия делает то, чего я не ожидаю? Какие преимущества дает магия, о которой я должен знать?

Ответ 1

Когда foo() возвращает обычный итеративный, эти два эквивалентны. "Магия" вступает в игру, когда foo() также является генератором. В этот момент случаи yield from foo() и for x in foo(): yield x существенно отличаются.

Генератор также может быть отправлен с помощью метода generator.send(). Когда вы используете цикл for, выражение yield x получает принятые данные; генератор foo() никогда не увидит этого. Но когда вы используете yield from, отправляемые данные поступают прямо к какому-либо выражению yield, делегированный генератору в настоящее время приостановлен. Другими словами, yield from передает отправленные данные, чтобы вместо этого делегированный генератор мог получить его.

Вы также можете создавать исключения в генераторе, generator.throw(); с флагом цикла for исключение возникает из строки yield x, а при yield from исключение снова передается; исключение создается внутри foo().

Вместе это означает, что yield from по существу заменяет текущий генератор на время делегированной итерации.

Генерируемый делегированный генератор также получает возможность связываться с родительским генератором, когда завершенный атрибут .value генерируемого исключения StopIteration возвращается как значение выражения yield from. Вы можете установить значение этого исключения, используя return <expression> в генераторе делегированного-to foo(), или вы можете явно использовать raise StopIteration(<expression>).

yield from был введен на язык с PEP 380: синтаксис для делегирования в подгенератор.