[Python-ideas] Map and filter should also convert StopIteration to RuntimeError

Terry Reedy tjreedy at udel.edu
Sat Dec 13 01:09:13 CET 2014


On 12/12/2014 4:24 PM, Alexander Belopolsky wrote:
>
> On Fri, Dec 12, 2014 at 4:14 PM, Terry Reedy
> <tjreedy at udel.edu
> <mailto:tjreedy at udel.edu>> wrote:
>
>     I propose that map.__next__ convert StopIteration raised by func to
>     RuntimeError
>
>
> Aren't we stepping on a slippery slope here?  What about say
>
>  >>> def f(x, y):
> ...     if x > 2:
> ...         raise StopIteration
> ...     return x + y

I consider this a wretched 'function'.  If an input value is outside the 
function's domain, making 'return value' impossible, the function should 
raise TypeError or ValueError.  As it is, it mixes a function with a 
hard-coded takewhile.  (A similar comment applies to the example I 
copied from Oscar.)  The two 'functions' should be kept separate.

 >>> import itertools as its
 >>> list(its.accumulate(its.takewhile(lambda x: x <= 2, range(10)), 
lambda x,y: x+y))
[0, 1, 3]

>  >>> import itertools
>  >>> itertools.accumulate(range(10), f)
> <itertools.accumulate object at 0x10acc0fc8>
>  >>> list(_)
> [0, 1, 3]

To me, this is a bug.  accumulate should accumulate until the iterable 
is exhauted.  (Or the doc should change to say that it accumulates until 
iterable is exhausted or func raises StopIteration.)  If accumulate 
cannot continue, I think it should raise something other than StopIteration.

The doc says that accumulate is equivalent to

def accumulate(iterable, func=operator.add):
     it = iter(iterable)
     total = next(it)
     yield total
     for element in it:
         yield func(total, element)

In 3.5, the StopIteration raised by f above will become RuntimeError. 
If accumulate.__next__ is not changed to match, then the equivalent 
would have to be given as

def accumulate(iterable, func=operator.add):
     it = iter(iterable)
     total = next(it)
     yield total
     for element in it:
         try:
             yield func(total, element)
         except StopIteration:
             return

Yes, similar considerations apply to all the itertools classes that call 
user functions: dropwhile, filterfalse, groupby, starmap, and takewhile.

"itertools.dropwhile(predicate, iterable)
     Make an iterator that drops elements from the iterable as long as 
the predicate is true; afterwards, returns every element."

If predicate raises before becoming false, dropwhile cannot 'return 
every element after'.  Should dropwhile simply pass through 
StopIteration, falsely saying that there are no more elements, or should 
it convert StopIteration to an exception that says 'something is wrong'? 
  (It could also ignore exceptions from predicate, but I do not suggest 
that.)

-- 
Terry Jan Reedy



More information about the Python-ideas mailing list