55

Here's the syntax for iterators in Java (somewhat similar syntax in C#):

Iterator it = sequence.iterator();

while (it.hasNext()) {
    System.out.println(it.next());
}

Which makes sense. Here's the equivalent syntax in Python:

it = iter(sequence)
while True:
    try:
        value = it.next() 
    except StopIteration:
        break
    print(value)

I thought Exceptions were supposed to be used only in, well, exceptional circumstances.

Why does Python use exceptions to stop iteration?

1

4 Answers 4

52

There's a very Pythonic way to write that expression without explicitly writing a try-except block for a StopIteration:

# some_iterable is some collection that can be iterated over
# e.g., a list, sequence, dict, set, itertools.combination(...)

for value in some_iterable:
    print(value)

You can read up on the relevant PEPs 234 255 if you want to know more behind why StopIteration was introduced and the logic behind iterators.

A general principle in python is to have one way to do something (see import this), and preferably its beautiful, explicit, readable, and simple, which the pythonic method satisfies. Your equivalent code is only necessary as python doesn't give iterators a hasNext member function; preferring people to just loop through the iterators directly (and if you need to do something else to just try reading it and catch an exception).

This automatic catching of an StopIteration exception at the end of an iterator makes sense and is an analogue of the EOFError raised if you read past an end of file.

6
  • 6
    The "Pythonic" way sure seems a lot more like "for value in sequence:" rather than "for value in iter(sequence):".. Update post? Oct 5, 2011 at 16:12
  • 16
    @Yam: I agree. Its not pythonic to take an existing sequence and convert it into an iterator just to apply a for loop to it; the sequence is already an iterable, so the conversion of a list to a listiterator is pointless. I kept the first line only to follow NullUserException's starting point, to explain how you should loop over an iterator, which is the same way you should loop over any iterable (list, set, str, tuple, dict, file, generator, etc.). I could have done something like it = itertools.combinations("ABCDE", 2) to get a better example of a meaningful iterator.
    – dr jimbob
    Oct 5, 2011 at 18:18
  • 1
    it = iter(sequence) is not needed.
    – Caridorc
    Oct 8, 2015 at 20:37
  • 2
    @Caridorc - If you read the comments, you'd your point was addressed. It isn't needed and was done to follow the starting point of the question (where they were explicitly asking about iterators) and you do need iter to explicitly generate an iterator (try type([]) (list) vs type(iter([])) (listiterator)).
    – dr jimbob
    Oct 9, 2015 at 2:00
  • // , @drjimbob, you raise an excellent point in the second comment to this question. I'm a bit new to the advanced features of iterables, and I would not have caught that if I had not read the comments. I think it would benefit many of us poor self-educating souls if we could see that point about how the question itself could be changed to "The Python Way" as the first, main part of the answer. Mar 23, 2016 at 20:35
32

The reason why python uses an Exception to stop an iteration is documented in PEP 234:

It has been questioned whether an exception to signal the end of the iteration isn't too expensive. Several alternatives for the StopIteration exception have been proposed: a special value End to signal the end, a function end() to test whether the iterator is finished, even reusing the IndexError exception.

  • A special value has the problem that if a sequence ever contains that special value, a loop over that sequence will end prematurely without any warning. If the experience with null-terminated C strings hasn't taught us the problems this can cause, imagine the trouble a Python introspection tool would have iterating over a list of all built-in names, assuming that the special End value was a built-in name!

  • Calling an end() function would require two calls per iteration. Two calls is much more expensive than one call plus a test for an exception. Especially the time-critical for loop can test very cheaply for an exception.

  • Reusing IndexError can cause confusion because it can be a genuine error, which would be masked by ending the loop prematurely.

Note: the idiomatic python way to loop over a sequence is like this:

for value in sequence:
    print (value)
21

It's a difference in philosophy. The Pythonic design philosophy is EAFP:

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C...

8

It's just that the Java implementation has a hasNext() method so that you can check for an empty iterator before you perform a next(). When you do call next() on a Java iterator with no elements left, a NoSuchElementException is thrown .

So effectively, you can do a try..catch in Java like the try..except in Python. And yes, as per a previous answer, philosophy is very important in the Pythonic world.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.