Skip to main content

Copying, References, and Mutable Objects

This type of problem appears very frequently in the early stages of learning Python: why does original_list change when I modify new_list?

The reason is usually not "lists are weird" but that you got a reference to the same object.

Assignment is not a copy

original = [1, 2, 3]
new_list = original

new_list.append(4)

print(original) # [1, 2, 3, 4]
print(new_list) # [1, 2, 3, 4]

new_list = original doesn't copy the list; it makes both variables point to the same list object.

Shallow Copy

The following approaches all create a new outer list:

items = [1, 2, 3]

a = items[:]
b = list(items)
c = items.copy()

But they only copy one level

items = [[1, 2], [3, 4]]
copied = items.copy()

copied[0].append(99)

print(items) # [[1, 2, 99], [3, 4]]
print(copied) # [[1, 2, 99], [3, 4]]

Because although the outer list is new, the nested sub-lists inside still point to the same objects.

Deep Copy

If the object has nested structures inside, consider copy.deepcopy():

import copy

items = [[1, 2], [3, 4]]
copied = copy.deepcopy(items)

copied[0].append(99)

print(items) # [[1, 2], [3, 4]]
print(copied) # [[1, 2, 99], [3, 4]]

Why this matters

Because many objects in Python are mutable:

  • list
  • dict
  • set

If you haven't distinguished "am I modifying the original" from "am I modifying a copy", debugging later will be very painful.

My own decision process

  • Only wrote a = b: basically not a copy
  • Only copied the outermost layer: shallow copy
  • There's nesting inside, and you want complete independence: deep copy