A very common task is to iterate over a list and remove some items based on a condition. This article shows the different ways how to accomplish this, and also shows some common pitfalls to avoid.
Let's say we need to modify the list
a and have to remove all items that are not even. We have this little helper function to determine whether a number is even or not.
a = [1, 2, 2, 3, 4] def even(x): return x % 2 == 0
Option 1: Create a new list containing only the elements you don't want to remove
1a) Normal List comprehension
Use list comprehension to create a new list containing only the elements you don't want to remove, and assign it back to a.
a = [x for x in a if not even(x)] # --> a = [1, 3]
You can learn more about list compehension in this tutorial.
1b) List comprehension by assigning to the slice
The above code created a new variable
a. We can also mutate the existing list in-place by assigning to the slice
a[:]. This approach is more efficient and could be useful if there are other references to
a that need to reflect the changes.
a[:] = [x for x in a if not even(x)] # --> a = [1, 3]
The itertools module provides various functions for very efficient looping and also offers a method to filter items:
from itertools import filterfalse a[:] = filterfalse(even, a) # --> a = [1, 3]
Option 2: Loop over a copy
If you really want to keep the for-loop syntax, then you need to iterate over a copy of the list (A copy is simply created by using
a[:]). Now you can remove items from the original list if the condition is true.
for item in a[:]: if even(item): a.remove(item) # --> a = [1, 3]
What NOT to do
Do not loop over the same list and modify it while iterating!
This is the same code as above except that here we don't loop over a copy. Removing an item will shift all following items one place to the left, thus in the next iteration one item will be skipped. This can lead to incorrect results.
for item in a: if even(item): a.remove(item) # --> a = [1, 2, 3] !!!
Also, never modify the index while looping over the list!
This is incorrect because changing i inside the loop will NOT affect the value of i in the next iteration. This example also produces unwanted effects and even lead to IndexErrors like here.
for i in range(len(a)): if even(a[i]): del a[i] i -= 1 # --> IndexError: list index out of range