I'm curious as to what's happening here. Can someone who knows generators and coroutines well explain this code.
def b(): for i in range(5): yield i x = (yield) print(x) def a(): g = b() next(g) for i in range(4): g.send(5) print(next(g)) a()output
None 1 None 2 None 3 None 4but when I switch around lines 3 and 4: the lines yield i and x = (yield) , I get the following.
5 None 5 None 5 None 5 NoneI suspect the problem might me from trying to use the yield statement to both receive and send values in the same function. Is this not possible in python?
I have successfully written a couple of programs that use coroutines, so I am familiar with the way they work, but I am confused as to the way this snippet of code is behaving. Any insights into this would be appreciated.
Thanks
Edit: Thanks BrenBarn and unutbu for your answers. What's happening here makes more sense when you expand the problem out as such.
def b(): for i in range(5): yield i x = yield None def a(): g = b() print('* got', g.send(None) ) for i in range(4): print('+ got', g.send(5) ) print('- got', g.send(None)) a()I don't quite get what you're asking, but basically: when you use send , it causes the most-recently-reached yield expression in the generator to evaluate to the value you send. Note also that send advances the generator to the next yield . One thing that may be confusing you is that you are printing the value of x inside the generator, and you are printing the value of next(g) inside b , but the generator is also yielding values at g.send(5) , and you aren't printing those.
In your first case, your first send causes the yield i statement to evaluate to 5 inside b , but you don't use this value inside b (you don't assign yield i to anything), so it does nothing. Also, when you do send(5) , the generator is yielding None (from the x = (yield) line), but you don't print it so you don't know this. Then you advance the generator again with next(g) . The most-recently-reached yield is the x = yield , but next(g) passes no value, so x gets set to None.
In the second case, the parity of the calls is reversed. Now your first send does send to the x = yield line, so x gets set to 5. This send also yields the loop value in b , but you ignore this value in a and don't print it. You then print next(g) , which is None. On each subsequent send, b prints the value of x , which is always 5 because that's what you always send, and then a prints the next yielded value, which is always None (because that's what x = yield yields).
I don't quite get what you mean by "using the yield statement to both receive and send values in the same function". You can certainly do this, but you have to realize that: a) a value (None) is still sent even when you call next(g) ; and b) a value is still yielded when you call g.send(5) .