3

This question already has an answer here:

I find this behaviour in Python quite peculiar and I believe it can lead to many bugs especially if you have a function/method that takes in a list and returns another list after carrying out some operations on the elements in list.

x = []

val = (row for row in x) # create a generator
print(next(val)) # will raise StopIteration exception

for i in x:
    if not i:
        raise ValueError('The sequence is empty') # Exception is not raised
    print(i) # does nothing and does not raise an exception

If you try to iterate over a generator with next and it's empty or has reached the end, a StopIteration exception is raised, but this is not the same when you use a for loop to iterate over a list or any iterable in Python.

I know I could just write if not x: to check if the list is empty but I believe an empty iterable should raise an Exception when you try to iterate over it as there is no benefit looping it.

I want to know if there's any special reason for this behavior in the Language design or am I not considering another case where it might be useful. I think the same goes for Java too.

marked as duplicate by gnat, Daenyth, user40980, amon, GlenH7 Feb 24 '16 at 0:00

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 5
    That's not how iteration works. A set of 3 items should cause three actions to occur under for, a set of 2 items - two actions, a set with one item one action, and an empty set should result in nothing being done. Raising an exception is very different from doing nothing, and it would cause way more problems than it avoids. – Kilian Foth Feb 17 '16 at 13:12
  • @KilianFoth Thanks never analyzed it that way – danidee Feb 17 '16 at 13:27
4

The thing that I think you're missing here is that the StopIteration is what actually makes the for loop stop. This is why you can write custom iterators that work with Python's for loop transparently. It doesn't do any checking to see if the iterator is empty, and the for loop does not keep track of of the iterator's state. This is an effect of the iterator protocol, and was an intentional choice in the language design. You can read more about the iterator protocol on Python's docs site if you want to.

It also makes more sense if you think about the number of items in e.g. your list corresponding directly to the number of times your loop is executed.

-----------------------------------|
| No. of items | No. times executed|
------------------------------------
|      5       |         5         |
|      4       |         4         |
|      3       |         3         |
|      2       |         2         |
|      1       |         1         |
|      0       |         0         |
------------------------------------

If the for loop special-cased an empty iterable, this invariant would be lost.

It would also complicate the protocol on writing custom iterators, because you would have to have an extra method to signal to the for loop that your iterable is empty before the iteration began.

Here's a (stupid) example of a custom iterator that always acts like it's empty:

class EmptyIterator():
    def __iter__(self): return self
    def __next__(self): raise StopIteration

for blah in EmptyIterator():
    print('this is never reached')
try:
    next(EmptyIterator())
except StopIteration:
    print('Oh hi, I was empty so I raised this Exception for you.')
9

This is odd to me. Why should an empty collection be treated any differently?

Forcing the programmer to check if the collection is empty before doing things to it would be a widespread, problematic sort of burden. Worse, an empty collection is in no way exceptional.

Personally, I would rather have the occasional bug where a no-op happened because I forgot to check for the rare case where I wanted different behavior on an empty collection rather than the occasional bug where an exception crashes my app because I forgot to say if empty, do nothing everywhere I wanted that common, expected behavior.

I mean, do you think printing empty strings should throw exceptions?

  • No printing empty strings should not throw exceptions at all. but i guess i'll prefer when my program crashes telling me. Hey silly what are you trying to do, iterate over an empty list?. but from @KillianFoth's comment above i kind of understand why nothing happens – danidee Feb 17 '16 at 13:30
  • 2
    @danidee Warning you for doing silly stuff that are syntactically valid is the job of static code analysis tools – sakisk Feb 17 '16 at 14:50
1

To me, iterating over an empty sequence falls (approximately) in the same category as adding 0, or multiplying by 1. Sure, multiplying a value by 1 (or adding 0) accomplishes nothing. But it is a useful special case of "does nothing", in that it works and for the rare cases where we need an identity element, it is useful not having to special-case it.

Likewise with iterations. If I have a sequence of things, that I have picked out because they hold a special property, then want to do something to each thing, it is frequently more useful to allow the special case of "no things were special" as a normal thing and simply do nothing.

There's one thing wrong with your example, though. It is not that print i does nothing, it is that it is never executed. And that's also why your if not i: doesn't do anything. However, it would, if x was [False], [None], [0] and a few other values that are considered false in a boolean context.

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