What does a yield inside a yield do?
yield
has two forms, expressions and statements. They're mostly the same, but I most often see them in the statement
form, where the result would not be used.
def f():
yield a thing
But in the expression form, yield
has a value:
def f():
y = yield a thing
In your question, you're using both forms:
def f():
yield ( # statement
yield 1 # expression
)
When you iterate over the resulting generator, you get first the result of the inner yield expression
>>> x=f()
>>> next(x)
1
At this point, the inner expression has also produced a value that the outer statement can use
>>> next(x)
>>> # None
and now you've exhausted the generator
>>> next(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
To understand more about statements vs expressions, there are good answers in other stackoverflow questions: What is the difference between an expression and a statement in Python?
a
is a generator object. The first time you call next
on it, the body is evaluated up to the first yield
expression (that is, the first to be evaluated: the inner one). That yield
produces the value 1
for next
to return, then blocks until the next entry into the generator. That is produced by the second call to next
, which does not send any value into the generator. As a result, the first (inner) yield
evaluates to None
. That value is used as the argument for the outer yield
, which becomes the return value of the second call to next
. If you were to call next
a third time, you would get a StopIteration
exception.
Compare the use of the send
method (instead of next
) to change the return value of the first yield
expression.
>>> a = mygen()
>>> next(a)
1
>>> a.send(3) # instead of next(a)
3
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
A more explicit way of writing the generator would have been
def mygen():
x = yield 1
yield x
a = mygen()
print(a.send(None)) # outputs 1, from yield 1
print(a.send(5)) # makes yield 1 == 5, then gets 5 back from yield x
print(a.send(3)) # Raises StopIteration, as there's nothing after yield x
Prior to Python 2.5, the yield
statement provided one-way communication between a caller and a generator; a call to next
would execute the generator up to the next yield
statement, and the value provided by the yield
keyword would serve as the return value of next
. The generator
would also suspend at the point of the yield
statement, waiting for the next call to next
to resume.
In Python 2.5, the yield
statement was replaced* with the yield
expression, and generators acquired a send
method. send
works very much like next
, except it can take an argument. (For the rest of this, assume that next(a)
is equivalent to a.send(None)
.) A generator starts execution after a call to send(None)
, at which point it executes up to the first yield
, which returns a value as before. Now, however, the expression blocks until the next call to send
, at which point the yield
expression evaluates to the argument passed to send
. A generator can now receive a value when it resumes.
* Not quite replaced; kojiro's answer goes into more detail about the subtle difference between a yield
statement and yield
expression.