Python reserved word yield

2

What's the difference between the following codes?

def bottom():
    yield 42

def bottom():
    return (yield 42)
    
asked by anonymous 24.06.2018 / 06:14

2 answers

4

The first case returns a generator, which is an object that contains an enumerator, has a state where this enumerator is. Anyone calling the function can get the result from the enumerator. This is transparent and will give 42.

In the second case, the yield returns the enumerator of its expression right there and then the result of that enumerator is returned from the function, it is already 42.

In practice nothing changes, in this case, but the internal mechanism is quite different.

You can view more at: What is Yield for? .

    
24.06.2018 / 14:46
2

In short, this is the mechanism used for co-routines to return values in Python - details how it works as follows:

First, if the body of a function has the keyword yield , yield from or is declared as async def , return does not work as a normal return. What is returned when calling such a function as a normal function is a co-routine or a generator:

>>> def a():
...     return (yield 42)
...  

>>> a()
<generator object a at 0x7fc904b167d8>

The value of the result - which is in return - of a generator or co-routine appears as the .value attribute of the StopIteration Exception caused by the iterator.

That is, this code:

def a():
    return (yield 42)
try:
    b.send("final value")
except StopIteration as end:
    end2 = end

print(end2.value)

It will print "initial value". Note that this mechanism will rarely be used directly by the programmer, and is used internally by the asynchronous code support code.

The send method was also introduced in Python 2, a version after the creation of the keyword yield - I believe in Python 2.5 - and allows values to be sent back into the generator (as the final value of the expression yield ). From this mechanism, plus the .throw method that allows you to cause an exception where yield is, allowed the generators to start working as co-routines.

More direct use happens when the generator is used from within another generator, with the form yield from . In this case, the value returned as a result of yield from is the value of return of the innermost generator. That is, the code below prints "None" (which is the result of yield in the previous function when it is only used with next :

def c():
    value = yield from a()
    print(value)
    return None

[_ for _ in c()]

If you are viewing code that does not use asynchronous programming, and has return values within function bodies with yield , those generators are probably being used that way.

Thus, this mechanism of separating the return value within the exception that indicates the end of the generator was the found form of these co-routines generating a final value, while the yield within them starts to serve not to generate a significant value, as in the case of common generators, but to be a break point of these functions. Co-routines are always called, unlike generators, by a more complex code responsible for coordinating their call, and calling other co-routines - this is the mechanism used in asyncio - the official library for asynchronous code execution in Python. This coordinating code is the event loop of the program that works asynchronously.

In this case, the code that uses the generator is not called directly - it is called from within other co-routines with the keyword await , or is scheduled in the event loop , from so that it returns a result when it is finished. (Event loop) does all the coordination to catch the .value of StopIteration, or throw an exception in the co-routine correctly, and call other co-routines when the code is "paused" by a "yield from" or "await")

In Python 3.4, the use of the asyncio.coroutine fault decorator marked a generator using yield from to function as a co-routine. So if you have asynchronous code made to work with Python 3.4 (or emulate this with Python 2.7), it will make use of yield from to call other co-routines in your body, and use return to return the final result . A specialized syntax has been created from Python 3.5 - as described above. The co-routines are declared with async def instead of needing the coroutine decorator, and the await keyword is used in place of yield from . (In short, in other named co-routines, the value in the "return" is the "normally" returned for a function called in valor = await funcao() form.

I recently wrote another extensive answer where I talk about some more aspects of asynchronous programming - take a look there: Python asynchronous generators

    
25.06.2018 / 15:06