Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: yield and yield assignment

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] 
like image 674
Charlie Haley Avatar asked Aug 20 '15 21:08

Charlie Haley


People also ask

What is yield () in Python?

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.

What is difference between yield and return in Python?

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.

Is yield faster Python?

At least in this very simple test, yield is faster than append.

How yield is implemented in Python?

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.


1 Answers

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 Nones 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.

like image 102
Jeremy Brown Avatar answered Sep 22 '22 08:09

Jeremy Brown