0

Can someone explain how yield is working in the code below?

def countdown(n):
    print("Counting down from", n)
    while n >= 0:
        newvalue = (yield n)

        if newvalue is not None:
            n = newvalue
        else:
            n -= 1

c = countdown(5)
for n in c:
    print n
    if n == 4:
        c.send(2)

output

('Counting down from', 5)
5
4
1
0

I expected it to be

('Counting down from', 5)
5
4
2
1
0

Where is "2" getting lost?

Tracing these two events (receive and produce) simultaneously is getting a bit trickier. This is not a duplicate of python generator "send" function purpose? because that question is mostly focused on understanding the need for coroutines and how they defer from generators. My question is very specific to a problem with using coroutines and generators together.

ThinkGeek
  • 4,749
  • 13
  • 44
  • 91
  • Possible duplicate of [python generator "send" function purpose?](https://stackoverflow.com/questions/19302530/python-generator-send-function-purpose) – muru Apr 11 '19 at 06:55
  • @muru actually it is not a duplicate. I have updated the question please check it again. – ThinkGeek Apr 11 '19 at 06:56
  • The examples in the duplicate makes this clear. – muru Apr 11 '19 at 06:57
  • 1
    I would recommend you to check out the [`A Curious Course on Coroutines and Concurrency`](http://www.dabeaz.com/coroutines/Coroutines.pdf) presentation by David Beazley. – Abdul Niyas P M Apr 11 '19 at 06:58

1 Answers1

1

The duplicate question has the relevant documentation:

The send() method returns the next value yielded by the generator[.]

And this answer there illustrating how control moves back and forth with send() is useful here. First, add additional prints to illustrate what's happening:

def countdown(n):
    print("Counting down from", n)
    while n >= 0:
        newvalue = (yield n)
        print (newvalue, n) # added output

        if newvalue is not None:
            n = newvalue
        else:
            n -= 1

c = countdown(5)
for n in c:
    print n
    if n == 4:
        print(c.send(2))   # added output.

You'll see:

('Counting down from', 5)
5
(None, 5)
4
(2, 4)
2
(None, 2)
1
(None, 1)
0
(None, 0)

With the send(2), control passed to the function, newvalue = (yield n) is continued, and the loop goes once around to the next yield, stopping again, but the value yielded this time is returned by send(2), not by the next iteration of the for loop. There's your 2.

muru
  • 4,723
  • 1
  • 34
  • 78