How to modify a list in place in Python

Did you ever see a piece of code that looks like this?

The purpose of it is to remove any even numbers from the list numbers . And it looks like it should work, right?

The problem explained

For most of you, the mistake will already be obvious, but beginners can expect to scratch their heads when they examine the list and see that it still has some even numbers in it.

Here’s what we wanted to see:

What is going on here? We can illuminate the problem if we write out the code without any of the syntactic niceties of the Python for  loop. (Keep in mind that this is merely for explanatory purposes and is not the kind of code you should be writing.)

Be your own debugger

Let’s “step through” the while  loop to see exactly how the execution goes wrong.

  • On the first iteration, the loop counter  i  is equal to 0. 1 (the value of the first element in numbers ) is assigned to elem . 1 is not divisible by 2 so the if  block is not called.
  • On the second iteration, i  is equal to 1. 2 (the value of the second element in numbers) is assigned to elem . 2 is clearly divisible by 2, so the if  block is called and the second element is removed from the list. The length of the list is reduced by 1. The element that was at the third position is now at the second position and so on. The list now looks like this.

  • On the third iteration, i  is equal to 2. 3 (the value of the third element in numbers , is assigned to elem . The 2 that used to be the third element, but is now the second, has been skipped entirely. It can never be removed from the list.
  • The 4 in the sixth position of the initial list is skipped in the same way.

Now that you understand how the code is going wrong, let’s see how to fix it.

How to fix it

There are several ways to refactor the code to give us the output we want. Let’s examine them.

First, we could use a list comprehension to build up a new list that contains only the elements in numbers  that are not divisible by 2. This is by far the simplest and most common way to achieve what we want. Here’s what it looks like:

A subtlety of this method is that it creates a completely new list and makes numbers point to it. If you had another name pointing to the old list, it will still point there. Here’s what I mean:

That may or may not be a problem, depending on the program, but if it is crucial to modify the list in place without copying anything, we can iterate backwards. While we’re at it, we can use del with an index instead of remove, which needlessly searches through the list for the first element with the given value:

So which one of these options should you use? If you are ok with creating a completely new list, then you should use the list comprehension. It’s by far the clearest and most idiomatic solution. Otherwise, if modifying the original list in place is crucial, you should go for the second version.

Download Mastering Decorators

Mastering_decorators_cover

Enjoyed this article? Join the newsletter and get Mastering Decorators - a gentle 22-page introduction to one of the trickiest parts of Python.

Weekly-ish. No spam. Unsubscribe any time. Powered by ConvertKit
  • Sreeni S

    Here is another way to do the in place modification of the list:
    numbers = [1, 2, 2, 3, 4, 4, 5]

    for n in numbers[:]:
        if not n % 2:
            numbers.remove(n)

    • MoMo Incarnate

      this relies on you making a copy of the list. so its not really in place since you are using extra memory