list-comprehension throws a RuntimeError

186 Views Asked by At

Why does this code work well and does not throw exceptions?

def myzip(*args):
    iters = [iter(arg) for arg in args]
    try:
        while True:
            yield tuple([next(it) for it in iters])
    except StopIteration:
        return


for x, y, z in myzip([1, 2], [3, 4], [5, 6]):
    print(x, y, z)

But if this line

yield tuple([next(it) for it in iters])

replace by

yield tuple(next(it) for it in iters)

then everything stops working and throws a RuntimeError?

1

There are 1 best solutions below

0
On BEST ANSWER

This is a feature introduced in Python 3.5, rather than a bug. Per PEP-479, a RuntimeError is re-raised intentionally when a StopIteration is raised from inside a generator so that iterations based on the generator can now only be stopped if the generator returns, at which point a StopIteration exception is raised to stop the iterations.

Otherwise, prior to Python 3.5, a StopIteration exception raised anywhere in a generator will stop the generator rather than getting propagated, so that in case of:

a = list(F(x) for x in xs)
a = [F(x) for x in xs]

The former would get a truncated result if F(x) raises a StopIteration exception at some point during the iteration, which makes it hard to debug, while the latter would propagate the exception raised from F(x). The goal of the feature is to make the two statements behave the same, which is why the change affects generators but not list comprehensions.