[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