We know it’s wrong, but why?
Sources
- https://stackoverflow.com/questions/6260089/strange-result-when-removing-item-from-a-list-while-iterating-over-it
- https://stackoverflow.com/questions/1637807/modifying-list-while-iterating
Example 1
Here we demonstrate the effect of changing an iterable in an iterator loop body on the traversing iterator. We print everything for convenience.
l = list(range(10))
index = 1
for i in l:
print(f'Iteration {index}')
index += 1
print(f'List: \n{l}')
print(f'Iterator value: {i}')
print(f'Element pop: {l.pop(0)}')
print(f'List: \n{l}')
print(f'Element pop: {l.pop(0)}')
print(f'List: \n{l}')
print()
Iteration 1
List:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Iterator value: 0
Element pop: 0
List:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Element pop: 1
List:
[2, 3, 4, 5, 6, 7, 8, 9]
Iteration 2
List:
[2, 3, 4, 5, 6, 7, 8, 9]
Iterator value: 3
Element pop: 2
List:
[3, 4, 5, 6, 7, 8, 9]
Element pop: 3
List:
[4, 5, 6, 7, 8, 9]
Iteration 3
List:
[4, 5, 6, 7, 8, 9]
Iterator value: 6
Element pop: 4
List:
[5, 6, 7, 8, 9]
Element pop: 5
List:
[6, 7, 8, 9]
Iteration 4
List:
[6, 7, 8, 9]
Iterator value: 9
Element pop: 6
List:
[7, 8, 9]
Element pop: 7
List:
[8, 9]
Analysis
Notice:
- The iterator
idoes not communicate with the listl. - Thus, when the iterable
lchanged in iteration 2, the iterator does not care - it still points to the second element in the iterable. - This means that in the second iteration, our iterator points at
3even though it was originally the 4th element inl, simply because it is now the second element inl, and the iterator proceeds according to the order of iteration.
Example 2: Variation
Let’s examine the effect of changing the iterable inside the iterator loop body on the traversing iterator using a variant iterator.
l = list(range(10))
for index, elem in enumerate(l):
print(f'Iteration {index}')
print(f'List: \n{l}')
print(f'Iterator index: {index}')
print(f'Iterator value: {elem}')
print(f'Element pop: {l.pop(0)}')
print(f'List: \n{l}')
print(f'Element pop: {l.pop(0)}')
print(f'List: \n{l}')
print()
Iteration 0
List:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Iterator index: 0
Iterator value: 0
Element pop: 0
List:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Element pop: 1
List:
[2, 3, 4, 5, 6, 7, 8, 9]
Iteration 1
List:
[2, 3, 4, 5, 6, 7, 8, 9]
Iterator index: 1
Iterator value: 3
Element pop: 2
List:
[3, 4, 5, 6, 7, 8, 9]
Element pop: 3
List:
[4, 5, 6, 7, 8, 9]
Iteration 2
List:
[4, 5, 6, 7, 8, 9]
Iterator index: 2
Iterator value: 6
Element pop: 4
List:
[5, 6, 7, 8, 9]
Element pop: 5
List:
[6, 7, 8, 9]
Iteration 3
List:
[6, 7, 8, 9]
Iterator index: 3
Iterator value: 9
Element pop: 6
List:
[7, 8, 9]
Element pop: 7
List:
[8, 9]
Analysis
Here we see exactly the “ignorance” of the iterator according to the index variable:
- The iterator does as its supposed to: Element 1 -> Element 2 -> … -> Iterable End
- Changing
ldoes not affect the iterator traversal, but it can affect what the iterator finds in those positions.