Search code examples
pythonlistfor-looppython-internals

What happens when you try to delete a list element while iterating over it


I'm iterating a list as follows:

some_list = [1, 2, 3, 4]
another_list = [1, 2, 3, 4]

for idx, item in enumerate(some_list):
    del some_list[idx]

for item in another_list:
    another_list.remove(item)

When I print out the contents of the lists

>>> some_list
[2, 4]
>>> another_list
[2, 4]

I'm aware that Python doesn't support modifying a list while iterating over it and the right way is to iterate over copy of list instead. But I want to know what exactly happens behind the scenes i.e. Why is the output of the above snippet [2, 4]?


Solution

  • You can use a self-made iterator that shows (in this case prints) the state of the iterator:

    class CustomIterator(object):
        def __init__(self, seq):
            self.seq = seq
            self.idx = 0
    
        def __iter__(self):
            return self
    
        def __next__(self):
            print('give next element:', self.idx)
            for idx, item in enumerate(self.seq):
                if idx == self.idx:
                    print(idx, '--->', item)
                else:
                    print(idx, '    ', item)
            try:
                nxtitem = self.seq[self.idx]
            except IndexError:
                raise StopIteration
            self.idx += 1
            return nxtitem
    
        next = __next__  # py2 compat
    

    Then use it around the list you want to check:

    some_list = [1, 2, 3, 4]
    
    for idx, item in enumerate(CustomIterator(some_list)):
        del some_list[idx]
    

    This should illustrate what happens in that case:

    give next element: 0
    0 ---> 1
    1      2
    2      3
    3      4
    give next element: 1
    0      2
    1 ---> 3
    2      4
    give next element: 2
    0      2
    1      4
    

    It only works for sequences though. It's more complicated for mappings or sets.