mathieu
mathieu

Reputation: 3174

sending and receiving values on the same generator

I am trying to understand some of the subtle details of python generators. One of the test programs I wrote to see if I could both send and read alternatively values to/from the same generator is the following:

def injector():
    while True:
        try:
            print 'a'
            v = yield
            print 'b', v
            yield v
            print 'c'
        except GeneratorExit:
            print 'exit'
            break

g = injector()

print 'send none'
g.send(None)
print 'send 2'
g.send(2)
print 'receiving'
v = g.next()
print 'received', v

g.close()

The expected output for this program is:

send none
a
send 2
b 2
receiving
received 2
c
a
exit

The output I get is:

send none
a
send 2
b 2
receiving
c
a
received None
exit

Now, obviously, the question is why am I getting the above output ? What is it that I did not understand about how generators work ?

Upvotes: 2

Views: 143

Answers (2)

glglgl
glglgl

Reputation: 91017

Let me try to clarify:

def injector():
    while True:
        try:
            print 'a'
            v = yield
            print 'b', v
            yield v
            print 'c'
        except GeneratorExit:
            print 'exit'
            break

g = injector()

print 'send none'
g.send(None)

Here the coroutine is started. It executes until the first yield whose result is returned from .send(), but then discarded.

The coroutine prints a and yields nothing, thus None. So it is ok to discard.

print 'send 2'
g.send(2)

Here you send a 2 to the coroutine, making it continue where you left it. v = 2.

It prints 2 and yields v again. You would expect that from the g.send() call. So after receiving and discarding v, you do

print 'receiving'
v = g.next()

Here you give control to the coroutine again, which prints c, then a, then yields None again, which you get here.

print 'received', v

thus prints None for v.

What you probably want is

g = injector()

print 'send none'
g.send(None)
print 'send 2'
v = g.send(2)
print 'received', v

g.close()

(Note that this last block can be written more cleanly and nicely as follows:

from contextlib import closing
with closing(injector()) as g:
    print 'send none'
    g.send(None)
    print 'send 2'
    v = g.send(2)
    print 'received', v

)

Upvotes: 3

PearsonArtPhoto
PearsonArtPhoto

Reputation: 39698

So, the issue you are having is the last call. First of all, it appears to be starting where the code left off, printing the 'c' first. Then as the statement was not yielded, it continues in the while loop, printing 'a'. Finally, the output is produced. The none results because nothing is yielded after the 'a'

Upvotes: 0

Related Questions