2

In python I came across a problem with the list.remove method. It wasn't removing what logically has to be there, and huge chunks were left in the list.

import os

def removeAll(content):
        for afile in content:
            content.remove(afile)
        return content
content = removeAll(os.listdir("C:\\Windows\\System32"))
print(content)

But if I were to remove single content without the loop

content.remove(content[123]) #If this string hasn't already been removed.

It would work. Why is this? What's an ideal alternative for dealing with huge lists?

Anshul Goyal
  • 73,278
  • 37
  • 149
  • 186
Shane
  • 267
  • 1
  • 5
  • 13

3 Answers3

3

This is not a bug. See Remove items from a list while iterating on ways to doing this perfectly.

A list is a mutable data type. If you delete elements from it, the length (and possibly indexes of elements) of the list will change. In your second example, were you to print the length of content before and after the removal, you would see the difference, because you are not assuming the old indexes and lengths to be valid for the list.


Consider the example below to understand more on when this exception happens:

>>> content = range(4)
>>> content
[0, 1, 2, 3]
>>> len(content)
4
>>> count = 0
>>> length = len(content)
>>> while count < length:
...     print count, length, content, len(content)
...     content.remove(content[count])
...     count += 1
... 
0 4 [0, 1, 2, 3] 4
1 4 [1, 2, 3] 3
2 4 [1, 3] 2
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
IndexError: list index out of range

It is clear that the length you are iterating on is a constant, but the value of count is simply corresponding to an index position which doesn't exist in the list anymore.


To work around this mutable nature of a list, you can remove elements from the end of the list:

>>> content = range(4)
>>> count = len(content) - 1
>>> while count >= 0:
...     print count, length, content, len(content)
...     content.remove(content[count])
...     count -= 1
... 
3 4 [0, 1, 2, 3] 4
2 4 [0, 1, 2] 3
1 4 [0, 1] 2
0 4 [0] 1
>>> print content
[]

Or you can pop / remove the first element always, as the other answers point.

Community
  • 1
  • 1
Anshul Goyal
  • 73,278
  • 37
  • 149
  • 186
  • Also, `count < length` never changes - infinite loop. – TigerhawkT3 Apr 12 '15 at 06:14
  • @muç„¡ Explain why a.remove(x) when x is a legitiment value, doesn't work – Shane Apr 12 '15 at 06:22
  • So, in python 3 (i dont remember which specific version), iterating through dicts is supposed to throw an error if you attempt to pop the items. I'd considered this a bug at that point because it's not throwing an error for a list while removing. – dtc Mar 20 '20 at 23:45
2

You can try this using pop or remove.

for file in range(len(content)):
    content.pop(0) # return 0th element in every iteration.

or

for file in range(len(content)):
    content.remove(content[0]) #just removes the oth element in every iteration
itzMEonTV
  • 19,851
  • 4
  • 39
  • 49
1

If you want to empty a list, slice everything and replace it with nothing:

content[:] = []
Peter Wood
  • 23,859
  • 5
  • 60
  • 99