I was looking at http://www.dabeaz.com/coroutines/, which I am finding very interesting, but in an example there is a behavior I do not understand.
In the bogus.py example, reported here
# bogus.py
#
# Bogus example of a generator that produces and receives values
def countdown(n):
print "Counting down from", n
while n >= 0:
newvalue = (yield n)
# If a new value got sent in, reset n with it
if newvalue is not None:
n = newvalue
else:
n -= 1
# The holy grail countdown
c = countdown(5)
for x in c:
print x
if x == 5:
c.send(3)
The sequence of numbers generated is 5, 2, 1, 0, and I can not understand where the number 3 is gone: after the send(3)
, the variable n
is correctly set, but at the second execution of yield
, it looks like the value 3 is just non yielded to the for loop.
Can someone clarify me why this happen?
The 3
was returned from .send()
, but discarded. The generator produces 5
, 3
, 2
, 1
, 0
; but because the 3
is returned to the .send()
call you don't see that value printed. The for
loop never gets to see it.
What happens is this:
- first time the
for
loop calls next()
on the generator, the code advances until 5
is yielded.
x == 5
is True
, so c.send(3)
is called. The code advances through the generator function, and newvalue
is set to 3
.
- The generator does not pause there, it now has control. The generator runs through the
while
loop and comes back to the (yield n)
expression. 3
is yielded. It becomes the return value for c.send(3)
. The return value is discarded here.
- The
for
loop continues, calls next()
again. The generator is continued again with yield
returning None
, loops round to n -= 1
and yielding 2
.
- The
for
loop continues to call next()
on the generator, 1
and 0
are yielded, the generator ends.
Qouting from the generator.send()
documentation:
Resumes the execution and “sends” a value into the generator function. The value
argument becomes the result of the current yield
expression. The send()
method returns the next value yielded by the generator, or raises StopIteration
if the generator exits without yielding another value.
Emphasis mine.