1

For loop with enumerate doesn't throw index out of range error while a an element is deleted inside loop?

L = [1, 4, 8, 5]
try:
  for i,item in enumerate(L): 
    print("Value of {} is {}".format(i, item))
    del L[i]
except IndexError as e:
    print("Index error: {err}.".format(err=e))

Output:

Value of 0 is 1
Value of 1 is 8

While this code causes the error

L = [1, 4, 8, 5]
try:
    for i in range(len(L)):
        print("Item:", L[i])
        del(L[i])
except IndexError as e:
    print("Error:", e)

Output:

Item: 1 
Item: 8
Error: list index out of range
5
  • I'm sorry, this isn't a duplicate (of that one at least). OP knows that there's a problem. OP just wants to know the difference of behaviour between the two. Commented Mar 30, 2018 at 14:01
  • 2
    I'd be surprised if we didn't have a proper duplicate somewhere, though -- variants on the question get asked a lot. Commented Mar 30, 2018 at 14:02
  • @Jean-FrançoisFabre Reopened, though I'm pretty sure the question I linked would help the OP, and virtually everyone else who gets here in the future. Commented Mar 30, 2018 at 14:04
  • @SvenMarnach if you check my answer, you'll see that I linked it as well :) Commented Mar 30, 2018 at 14:41
  • @CharlesDuffy there are questions asking what happens when removing while iterating. This one is (just slightly) different because it compares the behaviour of a dummy for loop using range with a for loop using an iterator. I found that different enough to answer it. Commented Mar 30, 2018 at 14:49

2 Answers 2

1

Both codes are wrong (see How to remove items from a list while iterating?), but the symptoms are different in both cases.

In the first case, you're iterating on L. Shortening L makes iteration end faster (notice that some items are skipped, because shortening the list confuses the internal iterator, which raises a StopIteration, which stops the for loop)

You could create some equivalent code of the first case using a while loop:

L = [1, 4, 8, 5]
try:
    i = 0
    while i < len(L):
        print("Item:", L[i])
        del(L[i])
        i += 1
except IndexError as e:
    print("Error:", e)

or emulating the fact that for loop catches the StopIteration exception raised by next on the enumerate iterator:

e = enumerate(L)
while True:
try:
   i,item = next(e)
   print("Value of {} is {}".format(i, item))
   del L[i]
except StopIteration:
    break

you get the same result, because len(L) is evaluated at each iteration, so the index cannot get out of bounds (but the result isn't very useful either)

In the second case, you've computed range(len(L)) with the initial list size. So after a while, the index is out of range for the shortened list.

The first case doesn't crash, but the result is hard to understand, implementation defined, cannot be relied on. Don't do this.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks Jean. I knew the first case is bad code. Just learning to understand it better. What does enumerate do so skipping items on delete end up in index error.
the while code I have added gives a good idea of what enumerate does. Well, it's not exactly like that (enumerate can work on a non-list iterable) but it looks very much like that.
Sorry to drag this further. I am just going mad :). From your statement : "Shortening L makes iteration end faster (notice that some items are skipped, because shortening the list confuses the internal iterator, which raises a StopIteration, which stops the for loop)". My question is simple, "Why exception StopIteration is not getting caught when this was iterated using for loop"
@Darshan For loops consume the StopIteration exception. This exception is part of the iterator protocol and indicates the end of the iteration. A for loop creates an iterator for the iterable you pass in and calls next() on that iterator until StopException is raised, at which point the loop stops.
0

In the second code with range, the interpreter actually expands your range loop so it will try to print(L[2]) while your list has only two value.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.