In Python most examples of yield from explain it with saying that
yield from foo()
is similar to
for x in foo(): yield x
When foo()
returns a regular iterable, the two are equivalent. The 'magic' comes into play when foo()
is a generator too. At that moment, the yield from foo()
and for x in foo(): yield x
cases differ materially.
A generator can be sent data too, using the generator.send() method. When you use the for
loop, the yield x
expression 'receives' the sent data; the foo()
generator will never see this. But when you use yield from
the sent data goes straight to whatever yield
expression the delegated-to generator is currently paused at. In other words, yield from
passes on the sent data so the delegated-to generator can receive it instead.
You can also raise exceptions in a generator, with generator.throw(); with the for
loop case, the exception is raised from the yield x
line, while with yield from
the exception is passed on again; the exception is raised inside foo()
instead.
Together, this means that yield from
in essence replaces the current generator for the duration of the delegated iteration.
The delegated-to generator also gets to communicate with the parent generator, when done the .value
attribute of the StopIteration
exception raised is returned as the value of the yield from
expression. You can set the value of that exception by using return <expression>
in the delegated-to foo()
generator, or you can use raise StopIteration(<expression>)
explicitly.
yield from
was introduced into the language with PEP 380: Syntax for Delegating to a Subgenerator.