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]
Yield is a keyword in Python that is used to return from a function without destroying the states of its local variable and when the function is called, the execution starts from the last yield statement. Any function that contains a yield keyword is termed a generator.
Yield is generally used to convert a regular Python function into a generator. Return is generally used for the end of the execution and “returns” the result to the caller statement. It replace the return of a function to suspend its execution without destroying local variables.
At least in this very simple test, yield is faster than append.
The yield instruction takes the current executing context as a closure, and transforms it into an own living object. This object has a __iter__ method which will continue after this yield statement. So the call stack gets transformed into a heap object.
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 a next()
call.
Using your example, this is what it would be like to use the couroutine functionality:
>>> def test1(x): ... for i in x: ... _ = yield i ... yield _ ... >>> l = [1,2,3] >>> gen_instance = test1(l) >>> #First send has to be a None >>> print gen_instance.send(None) 1 >>> print gen_instance.send("A") A >>> print gen_instance.send("B") 2 >>> print gen_instance.send("C") C >>> print gen_instance.send("D") 3 >>> print gen_instance.send("E") E >>> print gen_instance.send("F") Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
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:
When a generator function is resumed with a next() method, the current yield expression always evaluates to None.
next()
is used when using the iteration syntax.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With