Python Gotcha: List Copy Problems

Posted on Wed 20 December 2023 in Technical Solutions

Introduction

This problem isn't limited to only lists. Any mutable object in Python can be impacted by this "gotcha". The problem is setting one mutable object equal to another doesn't make a copy. It assigns a new variable to the same object as the original. Let's take a look and see how this appears.

The First Gotcha

The first example is pretty simple. Let's make a copy of our list

>>> list1 = [1,2,3,4,5]
>>> list2 = list1
>>> list1
[1, 2, 3, 4, 5]
>>> list2
[1, 2, 3, 4, 5]

That looks like I have two lists, both with the same set of values. But, let's modify the second list and change the first element in the list to be 6

>>> list2
[6, 2, 3, 4, 5]

But what about list1? It also has changed.

>>> list1
[6, 2, 3, 4, 5]

This is occuring because, as mentioned, list2 is assigned to the same object as list1. Any change to either, changes the object that both are assigned to.

>>> print(f"list1 id: {id(list1)}")
list1 id: 1497534800448
>>> print(f"list2 id: {id(list2)}")
list2 id: 1497534800448

The First Solution

This can be solved a few different ways. The important take away, though, is that you have to create a new object with the original values. I can think of a few ways to handle this. This article doesn't consider performance for these basic lists.

One option is to utilize the list() method and pass it the initial values from list1

>>> list3 = list(list1)
>>> list3
[6, 2, 3, 4, 5]
>>> list3[0] = 0
>>> list3
[0, 2, 3, 4, 5]
>>> list1
[6, 2, 3, 4, 5]
>>> print(f"list1 id: {id(list1)}")
list1 id: 1497534800448
>>> print(f"list3 id: {id(list3)}")
list3 id: 1497531282368

A second option is to slice() the entire original list into a new variable.

>>> list4 = list1[:]
>>> list4
[6, 2, 3, 4, 5]
>>> list4[0] = 0
>>> list4
[0, 2, 3, 4, 5]
>>> list1
[6, 2, 3, 4, 5]
>>> print(f"list1 id: {id(list1)}")
list1 id: 1497534800448
>>> print(f"list4 id: {id(list4)}")
list4 id: 1497534798208

Lastly, the list object has a copy() method that you can utilize.

>>> list5 = list1.copy()
>>> list5[0] = 0
>>> list5
[0, 2, 3, 4, 5]
>>> list1
[6, 2, 3, 4, 5]
>>> print(f"list1 id: {id(list1)}")
list1 id: 1497534800448
>>> print(f"list5 id: {id(list5)}")
list5 id: 1497534799552

With each of these three options, you can see that we created new objects because each has a unique object id. Changing one of these copies does not change the original's values.

The Second Gotcha

You, a savvy developer, know all this though. You also have a more advanced data structure. You have a list of lists.

[
    ['first element', 1, 2, 3],
    ['second element', 4, 5, 6],
    ['third element', 7, 8, 9],
]

Since you know about this gotcha, you are going to make a new object and copy the values to the new object.

>>> deeplist1 = [
...     ['first element', 1, 2, 3],
...     ['second element', 4, 5, 6],
...     ['third element', 7, 8, 9],
... ]
>>> deeplist2 = deeplist1.copy()
>>> print(f"deeplist1 id: {id(deeplist1)}")
deeplist1 id: 1497534571008
>>> print(f"deeplist2 id: {id(deeplist2)}")
deeplist2 id: 1497534577536

Now, with two different objects, is it safe to modify an element in one of the sublists? Turns out, it is not.

>>> deeplist2[0][0] = "NOT 3RD"
>>> deeplist2
[['NOT 3RD', 1, 2, 3], ['second element', 4, 5, 6], ['third element', 7, 8, 9]]
>>> deeplist1
[['NOT 3RD', 1, 2, 3], ['second element', 4, 5, 6], ['third element', 7, 8, 9]]

But why? The ids of deeplist1 and deeplist2 are different. This is true, but the sublists are not. Each sublist is assigned the same object id as the original.

>>> id(deeplist1[0][0]), id(deeplist2[0][0])
(1497534796864, 1497534796864)

The Second Solution

Utilize the deepcopy() method to make a copy of your original object.

>>> import copy
>>> deeplist3 = copy.deepcopy(deeplist1)
>>> deeplist3[0][0] = "NEW VALUE"
>>> deeplist3
[['NEW VALUE', 1, 2, 3], ['second element', 4, 5, 6], ['third element', 7, 8, 9]]
>>> deeplist1
[['NOT 3RD', 1, 2, 3], ['second element', 4, 5, 6], ['third element', 7, 8, 9]]

Using deepcopy() we can safely make a copy of our list of lists and modify both the original and the copy without impacting the other.


- is a father, an engineer and a computer scientist. He is interested in online community building, tinkering with new code and building new applications. He writes about his experiences with each of these.