pythonfunctionreturnyield

Why can't I use yield with return?


I would like you to consider the following code:

def func(alist):
    if len(alist) == 1:
        return alist[0] * 2
    for item in alist:
        yield item * 2

When I run func([1]), I don't get 2:

>>> func([1])
<generator object func at 0x7f0b9d09c660>

On Python 2, it doesn't even compile:

SyntaxError: 'return' with argument inside generator

Now, I realize that I cannot do this. However, I would like to know why. What exactly is going on behind the scenes that is causing Python to not give me 2?


Solution

  • Python has to decide whether a function is a generator at bytecode compilation time. This is because the semantics of generators say that none of the code in a generator function runs before the first next call; the generator function returns a generator iterator that, when next is called, runs the generator code. Thus, Python can't decide whether a function should be a generator or not by running it until it hits a yield or a return; instead, the presence of a yield in a function signals that the function is a generator.

    On Python 3, this isn't a SyntaxError any more, but your function is still unconditionally a generator function. return with an argument just means something in a generator now, instead of being a syntax error - it determines what yield from over the generator will return. Iterating over func([1]) will produce no elements, since the generator never yields.