On 9 October 2012 22:37, Terry Reedy <tjreedy@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