59

I'm curious about the difference between using raise StopIteration and a return statement in generators.

For example, is there any difference between these two functions?

def my_generator0(n):
    for i in range(n):
        yield i
        if i >= 5:
            return

def my_generator1(n):
    for i in range(n):
        yield i
        if i >= 5:
            raise StopIteration

I'm guessing the more "pythonic" way to do it is the second way (please correct me if I'm wrong), but as far as I can see both ways raise a StopIteration exception.

Alex Riley
  • 169,130
  • 45
  • 262
  • 238
slallum
  • 2,161
  • 4
  • 24
  • 24
  • 4
    Explicit or implicit ('off the end') `return` is the intended way to terminate a generator. If PEP 479 is accepted, the 'raise StopIteration' version will eventually not work as it does now. – Terry Jan Reedy Nov 20 '14 at 23:41
  • 1
    Note that [PEP 479 "Change StopIteration handling inside generators"](https://www.python.org/dev/peps/pep-0479/) was accepted into Python 3.5 in November 2014. – Anon Jan 03 '19 at 17:04

3 Answers3

64

There's no need to explicitly raise StopIteration as that's what a bare return statement does for a generator function - so yes they're the same. But no, just using return is more Pythonic.

From: http://docs.python.org/2/reference/simple_stmts.html#the-return-statement (valid to Python 3.2)

In a generator function, the return statement is not allowed to include an expression_list. In that context, a bare return indicates that the generator is done and will cause StopIteration to be raised.

Or as @Bakuriu points out - the semantics of generators have changed slightly for Python 3.3, so the following is more appropriate:

In a generator function, the return statement indicates that the generator is done and will cause StopIteration to be raised. The returned value (if any) is used as an argument to construct StopIteration and becomes the StopIteration.value attribute.

Jon Clements
  • 138,671
  • 33
  • 247
  • 280
  • 4
    In python3 the return can have an expression-list argument: http://docs.python.org/3.3/reference/simple_stmts.html#the-return-statement – Bakuriu Jan 06 '13 at 15:59
  • 3
    Note: if you raise a StopIteration exception explicitly in your generator there may be some methods (e.g. Python 3's multiprocessing imap_unordered()) that go on to error because they were expecting to receive a regular return (or an implicit return) and were going to raise StopIteration themselves. See @terry-jan-reedy's comment over in https://stackoverflow.com/questions/14183803/what-is-the-difference-between-raise-stopiteration-and-a-return-statement-in-gen#comment42620561_14183803 for the rationale. – Anon Jan 03 '19 at 17:00
27

As of late 2014 return is correct and raise StopIteration for ending a generator is on a depreciation schedule. See PEP 479 for full details.

Abstract

This PEP proposes a change to generators: when StopIteration is raised inside a generator, it is replaced with RuntimeError. (More precisely, this happens when the exception is about to bubble out of the generator's stack frame.) Because the change is backwards incompatible, the feature is initially introduced using a __future__ statement.

Acceptance

This PEP was accepted by the BDFL on November 22…

Rationale

The interaction of generators and StopIteration is currently somewhat surprising, and can conceal obscure bugs. An unexpected exception should not result in subtly altered behaviour, but should cause a noisy and easily-debugged traceback. Currently, StopIteration raised accidentally inside a generator function will be interpreted as the end of the iteration by the loop construct driving the generator.

Community
  • 1
  • 1
Blake Walsh
  • 1,501
  • 14
  • 10
5

That's true, they are equivalent except that one is readable whereas the other is obscure. This dates back to the very first version of generators (PEP 255, under "Specification: Return"), and the subsequent enhancements of (such as coroutines) do not change this. 3.3's yield from (PEP 380) extends that to return <expr> as syntactic sugar for raise StopIteration(<expr>), but that doesn't change the meaning of return;.