On 11/6/2014 5:15 AM, Steven D'Aprano wrote:
On Thu, Nov 06, 2014 at 07:47:09AM +1000, Nick Coghlan wrote:
And having said that... what if we introduced UnexpectedStopIteration but initially made it a subclass of StopIteration?
We could issue a deprecation warning whenever we triggered the StopIteration -> UnexpectedStopIteration conversion, pointing out that at some point in the future (3.6? 3.7?), UnexpectedStopIteration will no longer be a subclass of StopIteration (perhaps becoming a subclass of RuntimeError instead?).
I'm sorry, I have been trying to follow this thread, but there have been too many wrong turns and side-tracks for me to keep it straight. What is the problem this is supposed to solve?
Is it just that list (and set and dict) comprehensions treat StopIteration differently than generator expressions? That is, that
[expr for x in iterator]
list(expr for x in iterator)
are not exactly equivalent, if expr raises StopIteration.
If so, it seems to me that you're adding a lot of conceptual baggage and complication for very little benefit, and this will probably confuse people far more than the current situation does. The different treatment of StopIteration in generator expressions and list comprehensions does not seem to be a problem for people in practice, judging by the python-list and tutor mailing lists.
The current situation is simple to learn and understand:
(1) Generator expressions *emit* StopIteration when they are done:
py> next(iter([])) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
'iter([])' is a list_iterator, not a generator expression. Here is the example I think you wanted.
next(i for i in ()) Traceback (most recent call last): File "
", line 1, in <module> next(i for i in ()) StopIteration
(2) Functions such as tuple, list, set, dict *absorb* StopIteration:
py> list(iter([])) [] py> it = iter([]) py> list(next(it) for y in range(1000)) []
For-loops do the same, if StopIteration is raised in the "for x in iterable" header. That's how it knows the loop is done. The "for" part of a comprehension is the same.
(3) But raising StopIteration in the expression part (or if part) of a comprehension does not absord the exception, it is treated like any other exception:
py> [next(iter([])) for y in range(1000)] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <listcomp> StopIteration
If that is surprising to anyone, I suggest it is because they haven't considered what happens when you raise StopIteration in the body of a for-loop:
Which is the Python translation of the comprehension.
py> for y in range(1000): ... next(iter([])) ... Traceback (most recent call last): File "<stdin>", line 2, in <module> StopIteration
To me, the status quo is consistent, understandable, and predictable. ...
I agree. -- Terry Jan Reedy