Return and yield in the same function

What exactly happens, when yield and return are used in the same function in Python, like this?

def find_all(a_str, sub):
    start = 0
    while True:
        start = a_str.find(sub, start)
        if start == -1: return
        yield start
        start += len(sub) # use start += 1 to find overlapping matches

Is it still a generator?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Yes, it’ still a generator. The return is (almost) equivalent to raising StopIteration.

PEP 255 spells it out:

Specification: Return

A generator function can also contain return statements of the form:

"return"

Note that an expression_list is not allowed on return statements in
the body of a generator (although, of course, they may appear in the
bodies of non-generator functions nested within the generator).

When a return statement is encountered, control proceeds as in any
function return, executing the appropriate finally clauses (if any
exist). Then a StopIteration exception is raised, signalling that the
iterator is exhausted. A StopIteration exception is also raised if
control flows off the end of the generator without an explict return.

Note that return means “I’m done, and have nothing interesting to
return”, for both generator functions and non-generator functions.

Note that return isn’t always equivalent to raising StopIteration:
the difference lies in how enclosing try/except constructs are
treated. For example,

>>> def f1():
...     try:
...         return
...     except:
...        yield 1
>>> print list(f1())
[]

because, as in any function, return simply exits, but

>>> def f2():
...     try:
...         raise StopIteration
...     except:
...         yield 42
>>> print list(f2())
[42]

because StopIteration is captured by a bare “except”, as is any
exception.

Method 2

Yes, it is still a generator. An empty return or return None can be used to end a generator function. It is equivalent to raising a StopIteration(see @NPE’s answer for details).

Note that a return with non-None arguments is a SyntaxError in Python versions prior to 3.3.

As pointed out by @BrenBarn in comments starting from Python 3.3 the return value is now passed to StopIteration.

From PEP 380:

In a generator, the statement

return value

is semantically equivalent to

raise StopIteration(value)

Method 3

There is a way to accomplish having a yield and return method in a function that allows you to return a value or generator.

It probably is not as clean as you would want but it does do what you expect.

Here’s an example:

def six(how_many=None):
    if how_many is None or how_many < 1:
        return None  # returns value

    if how_many == 1:
        return 6  # returns value

    def iter_func():
        for count in range(how_many):
            yield 6
    return iter_func()  # returns generator

Method 4

Note: you don’t get StopIteration exception with the example below.

def odd(max):
    n = 0
    while n < max:
        yield n
        n = n + 1
    return 'done'


for x in odd(3):
    print(x)

The for loop catches it. That’s its signal to stop

But you can catch it in this way:

g = odd(3)

while True:
    try:
        x = next(g)
        print(x)
    except StopIteration as e:
        print("g return value:", e.value)
        break


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x