Here is a little function i wrote to understand the send method:
>>> def test():
... for x in xrange(10):
... res = yield
... y
There is no Error the first time, tested on python2.7 and python3.3:
>>> def test():
... for x in xrange(10):
... res = yield
... yield res
...
>>> a = test()
>>> next(a)
>>> next(a)
>>> next(a)
>>> next(a)
>>> a.send(0)
>>> a.send(0)
0
>>> a.send(0)
>>> a.send(0)
0
You can't send()
a value the first time because the generator did not execute until the point where you have the yield statement, so there is nothing to do with the value.
Here is the relevant paragraph from the pep that introduced the feature of co-routines with generators (http://www.python.org/dev/peps/pep-0342/):
Because generator-iterators begin execution at the top of the generator's function body, there is no yield expression to receive a value when the generator has just been created. Therefore, calling send() with a non-None argument is prohibited when the generator iterator has just started, and a TypeError is raised if this occurs (presumably due to a logic error of some kind). Thus, before you can communicate with a coroutine you must first call next() or send(None) to advance its execution to the first yield expression
def coro():
print 'before yield'
a = yield 'the yield value'
b = yield a
print 'done!'
c=coro() # this does not execute the generator, only creates it
# If you use c.send('a value') here it could _not_ do anything with the value
# so it raises an TypeError! Remember, the generator was not executed yet,
# only created, it is like the execution is before the `print 'before yield'`
# This line could be `c.send(None)` too, the `None` needs to be explicit with
# the first use of `send()` to show that you know it is the first iteration
print next(c) # will print 'before yield' then 'the yield value' that was yield
print c.send('first value sent') # will print 'first value sent'
# will print 'done!'
# the string 'the second value sent' is sent but not used and StopIterating will be raised
print c.send('the second value sent')
print c.send('oops') # raises StopIterating
The send()
can only be called if generator is waiting at yield. Since your generator hasn't started executing, calling send()
gives you the first error.
Further more, each yield in the generator consumes the value sent by send()
. When the second yield statement consumes value but it doesn't use it so that value is discarded. Then you wait on first yield which does consumes value from send()
and that value is printed out. So you end up with needing two send()
.
Here's the fixed version:
>>> def echo():
... while True:
... val = (yield)
... yield val
...
>>> g=echo()
>>> next(g) # move to 1st yield
>>> g.send(2) # execution stops at 2nd yield
2
>>> next(g) # execution stops at 1st yield
>>> g.send(3) # execution stops at 2nd yield
3