0

I am using python 3.5. When I tried to return a generator function instance and i am getting a StopIteration error. Why?

here is my code:

>>> def gen(start, end):
... '''generator function similar to range function'''
...    while start <= end:
...       yield start
...       start += 1
...
>>> def check(ingen, flag=None):
...    if flag:
...        for n in ingen:
...            yield n*2
...    else:
...        return ingen
...
>>> # Trigger else clause in check function
>>> a = check(gen(1,3))
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: <generator object gen at 0x7f37dc46e828>

It looks like the generator is somehow exhausted before the else clause is returns the generator.

It works fine with this function:

>>> def check_v2(ingen):
...     return ingen
...
>>> b = check_v2(gen(1, 3))
>>> next(b)
1
>>> next(b)
2
>>> next(b)
3

4 Answers4

3

In Python, if yield is present in a function, then Python treats it as a generator. In a generator, any return will raise StopIteration with the returned value. This is a new feature in Python 3.3: see PEP 380 and here. check_v2 works because it doesn't contain a yield and is therefore a normal function.

There are two ways to accomplish what you want:

  • Change the return to a yield in check.
  • Have the caller trap StopIteration, as shown below

    try:
        next(a)
    except StopIteration as ex:
        print(ex.value)
    
Community
  • 1
  • 1
kirbyfan64sos
  • 10,377
  • 6
  • 54
  • 75
2

When a generator hits its return statement (explicit or not) it raises StopIteration. So when you return ingen you end the iteration.

check_v2 is not a generator, since it does not contain the yield statement, that's why it works.

wroniasty
  • 7,884
  • 2
  • 32
  • 24
1

As others have said, if you return from a generator, it means the the generator has stopped yielding items, which raises a StopIteration whatever you return.

This means that check actually returns an empty iterator.

If you want to return the results of another generator, you can use yield from :

def check(ingen, flag=None):
    if flag:
        for n in ingen:
            yield n*2
    else:
        yield from ingen
pistache
  • 5,782
  • 1
  • 29
  • 50
  • 1
    Thank u for opening my eyes to the `yield from` syntax. +1 –  Aug 12 '16 at 16:02
  • 1
    interestingly it's also with `yield from` that coroutines are implemented (were, actually, now it's a native opcode) – pistache Aug 12 '16 at 16:12
0

See PEP 380:

In a generator, the statement

return value

is semantically equivalent to

raise StopIteration(value)

except that, as currently, the exception cannot be caught by except clauses within the returning generator.

This is a new feature in Python 3.3.

Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378