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
?
This is a feature introduced in Python 3.5, rather than a bug. Per PEP-479, a
RuntimeError
is re-raised intentionally when aStopIteration
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 aStopIteration
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:The former would get a truncated result if
F(x)
raises aStopIteration
exception at some point during the iteration, which makes it hard to debug, while the latter would propagate the exception raised fromF(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.