Python 3, yield expression return value influenced by its value just received via send()?
<p>after reading documentation, questions, and making my own test code, I believe I have understood how a <code>yield expression</code> works.</p>

<p>Nevertheless, I am surprised of the behavior of the following example code:</p>

<pre><code>def gen(n=0):
    while True:
        n = (yield n) or n+1

g=gen()
print( next(g) )
print( next(g) )
print( g.send(5) )
print( next(g) )
print( next(g) )
</code></pre>

<p><strong>I would have expected that it returned 0, 1, 2, 5, 6, while instead it produces: 0, 1, 5, 6, 7.</strong> </p>

<p>I.e: I would have expected that the <code>yield expression</code> produce these effects:</p>

<ol>
<li>calculate the value of the <code>yield expression</code> , and return it to the caller</li>
<li>get the value(s) from the caller's <code>send()</code> and use them in the as the value of the yield expression which the generator function code receives</li>
<li>suspend execution before anything else is executed; it will be resumed at the same point at the same <code>next(g)</code> or <code>g.send()</code> call</li>
</ol>

<p>... and/or that Python would care to avoid any interference between the two
flows of information in (1) and (2), i.e. that they were guaranteed independent such as in a tuple assignment <code>a, b = f(a,b), g(a,b)</code></p>

<p>(I would even wonder if it were better to make the suspension happen in between (1) and (2), but maybe it would be quite complicated because it would imply that only part of the statement is executed and the rest is held for the next resume)</p>

<p>Anyway, the order of the operations is rather (2), then (1), then (3), so that the assignment in (2) occurs before, and can influence the assignment in (1). I.e. the value injected by the <code>g.send()</code> call is used before calculating the yield expression itself, which is directly exposed to the caller as the value of the same <code>g.send()</code> expression.</p>

<p>I am astonished because from the point of view of the code in the generator expression, the value received in its <code>lhs</code> can influence the value taken by the <code>rhs</code>!</p>

<p>To me, this is kind of misleading because one expects that in a statement like <code>lhs expr = rhs expr</code>, all calculations in the <code>rhs expr</code> are finished before doing the assignment, and frozen during the assignment. It looks really weird that the <code>lhs</code> of an assignment can influence it's own <code>rhs</code>! </p>

<p><strong>The question: which are the reasons why it was made this way? Any clue?</strong></p>

<p>(I know that "We prefer questions that can be answered, not just discussed", but this is something in which I stumbled and made me consume a lot of time. I believe a bit of discussion won't to any bad and maybe will save someone else's time) </p>

<p>PS. of course I understand that I can separate the assignment into two steps, so that any value received from <code>send()</code> will be used only after resuming the operation. Like this:</p>

<pre><code>def gen(n=0):
    while True:
        received = (yield n)      
        n = received or (n+1)
</code></pre>

<p>Your confusion lies with <code>generator.send()</code>. Sending is <em>just the same thing as using <code>next()</code></em>, with the difference being that the <code>yield</code> expression produces a different value. Put differently, <code>next(g)</code> is the same thing as <code>g.send(None)</code>, both operations resume the generator there and then.</p>

<p>Remember that a generator <em>starts</em> paused, at the top. The first <code>next()</code> call advances to the first <code>yield</code> expression, stops the generator and then pauses. When a <code>yield</code> expression is paused and you call <em>either</em> <code>next(g)</code> or <code>g.send(..)</code>, the generator is resumed where it is right now, and then runs until the next <code>yield</code> expression is reached, at which point it pauses again.</p>

<p>For your code, this happens:</p>

<ul>
<li><code>g</code> is created, nothing happens in <code>gen()</code></li>
<li><code>next(g)</code> actually enters the function body, <code>n = 0</code> is executed, <code>yield n</code> pauses <code>g</code> and yields <code>0</code>. This is printed.</li>
<li><code>next(g)</code> resumes the generator; <code>None</code> is returned for <code>yield n</code> (nothing was sent after all), so <code>None or n + 1</code> is executed an <code>n = 1</code> is set. The loop continues on and <code>yield n</code> is reached again, the generator pauses and <code>1</code> is yielded. This is printed.</li>
<li><code>g.send(5)</code> resumes the generator. <code>5 or n + 1</code> means <code>n = 5</code> is executed. The loop continues until <code>yield n</code> is reached, the generator is paused, <code>5</code> is yielded and you print <code>5</code>.</li>
<li><code>next(g)</code> resumes the generator; <code>None</code> is returned (nothing was sent again), so <code>None or n + 1</code> is executed an <code>n = 6</code> is set. The loop continues on and <code>yield n</code> is reached again, the generator pauses and <code>6</code> is yielded and printed.</li>
<li><code>next(g)</code> resumes the generator; <code>None</code> is returned (nothing was sent again), so <code>None or n + 1</code> is executed an <code>n = 7</code> is set. The loop continues on and <code>yield n</code> is reached again, the generator pauses and <code>7</code> is yielded and printed.</li>
</ul>

<p>Given your steps 1., 2. and 3., the actual order is 3., 2., 1. then, with the addition that <code>next()</code> also goes through step <code>2.</code> producing <code>None</code>, and <code>1.</code> being the <em>next</em> invocation of <code>yield</code> encountered after un-pausing.</p>

python yield