7

Reading about Python coroutines, I came across this code:

def countdown(n):
    print("Start from {}".format(n))
    while n >= 0:
        print("Yielding {}".format(n))
        newv = yield n
        if newv is not None:
            n = newv
        else:
            n -= 1

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

which curiously outputs:

Start from 5
Yielding 5
5
Yielding 3
Yielding 2
2
Yielding 1
1
Yielding 0
0

In particular, it misses printing the 3. Why?


The referenced question does not answer this question because I am not asking what send does. It sends values back into the function. What I am asking is why, after I issue send(3), does the next yield, which should yield 3, not cause the for loop to print 3.

Ritzymon
  • 85
  • 5
  • First, it misses 4 as well. Second, what do you think `c.send(3)` does ? – Nir Alfasi Jul 19 '17 at 06:37
  • @alfasin It misses the 4 because of `c.send(3)`. The point is that the code should set `n` to 3, and then on the next loop, yield 3. But it does not get printed. Why? – Ritzymon Jul 19 '17 at 06:40
  • it becomes clearer if you add a `print('newv: {}'.format(newv)` in the `if newv is not None:` block – Maarten Fabré Jul 19 '17 at 07:35

2 Answers2

5

You never checked the return value of your c.send(3) call. In fact, it appears that the send method itself advances the generator, so your missing 3 was yielded in that c.send(3) call.

def countdown(n):
    while n >= 0:
        newv = yield n
        if newv is not None:
            n = newv
        else:
            n -= 1

c = countdown(5)
print(next(c))  
print(c.send(3))
print(next(c))  

The output would be:

5
3
2

It is actually documented:

generator.send(value)

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.

taras
  • 6,566
  • 10
  • 39
  • 50
0

You are failing in c.send(). You are comparing when n==5, but you are not sending 5 (in your function is n-1), you are sending 3. For instance, it not prints 4.

Try the same code but with c.send(5), it will print from 5 to 0.

c = countdown(5)
for n in c:
    print(n)
    if n == 5: c.send(5)
Víctor López
  • 819
  • 1
  • 7
  • 19
  • Actually, it doesn't print from 5 to 0. It prints `5`, `3`, `2`, `1`, `0`, missing the `4`. Same problem. – Ritzymon Jul 19 '17 at 06:54