[Python-ideas] Add stop=exception option to next() (was Re: PEP 479 and take())

Steven D'Aprano steve at pearwood.info
Sun Dec 14 02:55:27 CET 2014


On Sat, Dec 13, 2014 at 07:39:25PM -0500, Terry Reedy wrote:

[...]
> >>try:
> >>    item = next(it)
> >>except StopIteration:
> >>    raise ValueError('iterable must not be empty') from None
> >
> >I don't remember ever needing to write such code.
> 
> Others do, whenever the first item of an iterable needs special 
> treatment.  And others *have* forgotten to catch StopIteration when 
> calling next(it).

And if they forget that, what makes you think they will remember to 
write next(it, stop=ValueError)?


> The code for reduce *is* written with such code.
> 
> >>> from functools import reduce as r
> >>> r(lambda a, b: a+b, [])
> Traceback (most recent call last):
>   File "<pyshell#1>", line 1, in <module>
>     r(lambda a, b: a+b, [])
> TypeError: reduce() of empty sequence with no initial value
> 
> But the current equivalent code in the doc is buggy because it was not.

One has to expect that pedagogical examples often avoid complicating the 
code with error handling, and consequently are buggy compared to the 
real production code. It's almost a truism that an algorithm that takes 
5 lines in text books might take 50 lines in production code :-)


> def reduce(function, iterable, initializer=None):
>     it = iter(iterable)
>     if initializer is None:
>         value = next(it)
>     else:
>         value = initializer
>     for element in it:
>         value = function(value, element)
>     return value
> 
> >>> reduce(lambda a, b: a+b, [])
> Traceback (most recent call last):
>   File "C:\Programs\Python34\tem.py", line 11, in <module>
>     reduce(lambda a, b: a+b, [])
>   File "C:\Programs\Python34\tem.py", line 4, in reduce
>     value = next(it)
> StopIteration


Seems perfectly reasonable to me. Many user-written functions don't 
bother to catch exceptions and convert them to a standard exception, 
instead they just let them bubble up. That's not a bug, it's a feature 
of Python.

Generators are rather special, but for non-generator functions like 
reduce, StopIteration is just another exception, no different from 
ValueError, IndexError, UnicodeDecodeError, etc.


> The equivalent code now would be
> 
> try:
>     value = next(it)
> except StopIteration:
>     raise TypeError("reduce() of empty sequence with no initial value") 
> from None
> 
> http://bugs.python.org/issue23049?

I don't think that this example needs to be fixed, but I don't object if 
you do fix it.

> That a core developer would miss this 
[...]

You're assuming that it was an oversight. It's more likely that the 
author of that code snippet simply didn't care to complicate the code 
just for the sake of raising the same exception type as the built-in 
reduce.

If you read the docs for reduce, it doesn't make any promises about what 
exceptions it will raise. Raising ValueError is not a documented part of 
the API.

https://docs.python.org/3/library/functools.html#functools.reduce

So the Devil's Advocate might argue that the bug is not in the pure 
Python code but in the built-in reduce, since it wrongly raises 
ValueError when it should raise StopIteration.



-- 
Steven


More information about the Python-ideas mailing list