[Python-ideas] Propagating StopIteration value

Oscar Benjamin oscar.j.benjamin at gmail.com
Wed Oct 10 00:49:08 CEST 2012


On 9 October 2012 22:37, Terry Reedy <tjreedy at udel.edu> wrote:
> On 10/9/2012 11:34 AM, Serhiy Storchaka wrote:
>>
>> On 09.10.12 10:51, Greg Ewing wrote:
>>>
>>> Where we seem to disagree is on
>>> whether returning a value with StopIteration is part of the
>>> iterator protocol or the generator protocol.

Correct.

> Part of the iterator protocol is that .__next__ methods raise StopIteration
> to signal that no more objects will be yielded. A value can be attached to
> StopIteration, but it is irrelevant to it use as a 'done' signal. Any
> iterator .__next__ method.  can raise or pass along
> StopIteration(something). Whether 'something' is even seen or not is a
> different question. The main users of iterators, for statements, ignore
> anything extra.

I know this isn't going anywhere right now but since it might one day
I thought I'd say that I considered how it could be different and the
best I came up with was:

def f():
  return 42
  yield

for x in f():
  pass
else return_value:
   # return_value = 42 if we get here

> If the generator stops by raising StopIteration instead of returning None,
> *that* StopIteration instance is passed along by the .__next__ wrapper.
> (This may be an implementation detail, but it is currently true.)

I'm wondering whether propagating or not propagating the StopIteration
should be a documented feature of some iterator-based functions or
should always be considered an implementation detail (i.e. undefined
language behaviour). Currently in Python 3.3 I guess that it is always
an implementation detail since the behaviour probably results from an
implementation that was written under the assumption that
StopIteration instances are interchangeable.

> Since filter takes a single iterable, it can be written like g3 and not
> catch the StopIteration of the corresponding iterator.
>
> def filter(pred, iterable):
>   it = iter(iterable)
>   while True:
>     item = next(it)
>     if pred(item):
>       yield item
>   # never reaches here, never returns None
>
> Map takes multiple iterables. In 2.x, map extended short iterables with None
> to match the longest. So it had to swallow StopIteration until it had
> collected one for each iterator. In 3.x, map stops at the first
> StopIteration, so it probably could be rewritten to not catch it. Whether it
> makes sense to do that is another question.

Thanks. That makes more sense now as I hadn't considered this
behaviour of map before.


Oscar



More information about the Python-ideas mailing list