How does this code, involving assignment and the yield operator, work? The results are rather confounding.
def test1(x):
for i in x:
_ = yield i
yield _
def test2(x):
for i in x:
_ = yield i
r1 = test1([1,2,3])
r2 = test2([1,2,3])
print list(r1)
print list(r2)
Output:
[1, None, 2, None, 3, None]
[1, 2, 3]
The assignment syntax ("yield expression") allows you to treat the generator as a rudimentary coroutine.
First proposed in PEP 342 and documented here: https://docs.python.org/2/reference/expressions.html#yield-expressions
The client code that is working with the generator can communicate data back into the generator using its
send()
method. That data is accessible via the assignment syntax.send()
will also iterate - so it actually includes anext()
call.Using your example, this is what it would be like to use the couroutine functionality:
Note that some of the sends are lost because of the second
yield
in each loop iteration that doesn't capture the sent data.EDIT: Forgot to explain the
None
s yielded in your example.From https://docs.python.org/2/reference/expressions.html#generator.next:
next()
is used when using the iteration syntax.First it
yield
s the value referenced byi
, e.g.1
. Then it yields the value returned by theyield
operation, which isNone
. It does this on each iteration of the loop.This simply
yield
s the value referenced byi
, e.g.1
, then proceeds to the next iteration of the loop, producing2
, then3
.Unlike
return
, theyield
keyword can be used in an expression:Now, when the interpreter sees a
yield
, it will generate the indicated value. However, when it does so, that operation returns the valueNone
, just likemylist.append(0)
orprint('hello')
willreturn
the valueNone
. When you assign that result to a reference like_
, you're saving thatNone
.So, in the first snippet, you're yielding an object, then you save the "result" of that
yield
operation, which isNone
, and then youyield
thatNone
. In the second snippet, you yield an object, then you save the "result" of thatyield
operation, but you neveryield
that result, soNone
does not appear in the output.Note that
yield
won't always returnNone
- this is just what you sent to the generator withsend()
. Since that was nothing in this case, you getNone
. See this answer for more onsend()
.To expand on TigerhawkT3's answer, the reason that the yield operation is returning
None
in your code is becauselist(r1)
isn't sending anything into the generator. Try this:Output:
Here's a somewhat manufactured example where sending values into a generator could be useful:
Output: