[Python-ideas] Map and filter should also convert StopIteration to RuntimeError
Terry Reedy
tjreedy at udel.edu
Fri Dec 12 22:14:07 CET 2014
" exception StopIteration
Raised by built-in function next() and an iterator‘s __next__()
method to signal that there are no further items produced by the iterator."
I have always taken this to mean that these are the only functions that
should raise StopIteration, but other have not, and indeed,
StopIteration in generator functions, which are not __next__ methods,
has been accepted and passed on by generator.__next__. PEP 479 reverses
this acceptance by having generator.__next turn StopIteration raised in
a user-written generator function body into a RuntimeError. I propose
that other builtin iterator.__next__ methods that execute a passed in
function do the same.
This proposal comes from Oscar Benjamin's comments on the PEP in the
'Generator are iterators' thread. In one post, he gave this example.
>>> def func(x):
... if x < 0:
... raise StopIteration
... return x ** 2
...
>>> it = map(func, [1, 2, 3, -1, 2])
>>> list(it)
[1, 4, 9]
>>> list(it) # map continues to yield values...
[4]
Function func violates what I think should be the guideline. When
passed to map, the result is a silently buggy iterator. The doc says
that map will "Return an iterator that applies function to every item of
iterable, yielding the results.". If map cannot do that, because func
raises for some item in iterable, map should also raise, but the
exception should be something other than StopIteration, which signals
normal non-buggy completion of its task.
I propose that map.__next__ convert StopIteration raised by func to
RuntimeError, just as generator.__next__ now does for StopIteration
raised by executing a generator function frame. (And same for filter().)
class newmap:
"Simplified version allowing just one input iterable."
def __iter__(self):
return self
def __init__(self, func, iterable):
self.func = func
self.argit = iter(iterable)
def __next__(self):
func = self.func
args = next(self.argit) # pass on expected StopIteration
if func is None:
return args
else:
try: # new wrapper
return func(args)
except StopIteration:
raise RuntimeError('func raised StopIteration')
--
Terry Jan Reedy
More information about the Python-ideas
mailing list