
On 1 July 2013 10:59, Steven D'Aprano <steve@pearwood.info> wrote:
So I think the missing piece is that generators are actually iterators. Since raising StopIteration is the official way to halt an iterator, it's also the (or at least, an) official way to halt a generator, and not a quirk of the implementation.
Yeah, the reason Andrew's proposed fix to the comprehension semantics makes sense is the fact that exactly *where* StopIteration gets raised during a "__next__" invocation is supposed to be completely opaque from the point of view of the iterator protocol: while True: try: x = next(itr): except StopIteration: break # Process x... The caught "StopIteration" could come from: - a generator iterator frame terminating - a generator iterator explicitly raising StopIteration - a sequence iterator triggering IndexError - a sentinel iterator noticing the sentinel value - any other __next__ method raising StopIteration When I did the conversion to "make [x for x in y] merely an optimised version of list(x for x in y)" change for Python 3, I know I missed the fact that part of that change involved moving the evaluation of all of the subexpressions inside the implicit try/except that is part of the iterator protocol, and I don't recall anyone else bringing it up either. Even if it did come up, we must have dismissed it as introducing too much overhead to set up the almost-certainly-unnecessary try/except for each iteration. Fortunately, Andrew is right that we can avoid that overhead and use a single try/except to cover the whole comprehension, which is a nice and cheap change. Cheers, Nick.