Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use return (yield something)?

Tags:

For a long time I didn't know you can't put return in front of a yield statement. But actually you can:

def gen():     return (yield 42) 

which is similar to

def gen():     yield 42     return 

And the only usage I can think of is to attach sent value to StopIteration: pep-0380

return expr in a generator causes StopIteration(expr) to be raised upon exit from the generator.

def gen():     return (yield 42)  g = gen() print(next(g))  # 42 try:     g.send('AAAA') except StopIteration as e:     print(e.value)  # 'AAAA' 

But this can be done using an extra variable too, which is more explicit:

def gen():     a = yield 42     return a  g = gen() print(next(g)) try:     g.send('AAAA') except StopIteration as e:     print(e.value)  # 'AAAA' 

So it seems return (yield xxx) is merely a syntactic sugar. Am I missing something?

like image 900
laike9m Avatar asked Feb 12 '17 05:02

laike9m


People also ask

Why do we use yield?

The yield statement suspends function's execution and sends a value back to the caller, but retains enough state to enable function to resume where it is left off. When resumed, the function continues execution immediately after the last yield run.

Why do we use yield in Python?

yield in Python can be used like the return statement in a function. When done so, the function instead of returning the output, it returns a generator that can be iterated upon. You can then iterate through the generator to extract items.

What is the difference between return and yield 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.

Can I use yield and return in same function?

"return" and "yield" should not be used in the same function.


1 Answers

Inside a generator the expressions (yield 42) will yield the value 42, but it also returns a value which is either None, if you use next(generator) or a given value if you use generator.send(value).

So as you say, you could use an intermediate value to get the same behavior, not because this is syntactical sugar, but because the yield expressions is literally returning the value you send it.

You could equally do something like

def my_generator():     return (yield (yield 42) + 10) 

If we call this, using the sequence of calls:

g = my_generator() print(next(g)) try:     print('first response:', g.send(1))     print('Second response:', g.send(22))     print('third response:', g.send(3)) except StopIteration as e:     print('stopped at', e.value) 

First we get the output of 42, and the generator is essentially paused in a state you could describe like: return (yield <Input will go here> + 10), If we then call g.send(1) we get the output 11. and the generator is now in the state: return <Input will go here>, then sending g.send(22) will throw a StopIteration(22), because of the way return is handled in generators. So you never get to the third send because of the exception.

I hope this example makes it a bit more apparent how yield works in generators and why the syntax return (yield something) is nothing special or exotic and works exactly how you'd expect it.

As for the literal question, when would you do this? Well when ever you want to yield something, and then later return a StopIteration echoing the input of the user sent to the generator. Because this is literally what the code is stating. I expect that such behavior is very rarely wanted.

like image 88
jVincent Avatar answered Sep 29 '22 18:09

jVincent