The problem

When you copy a list containing other lists, a simple assignment doesn't create a true copy—both variables point to the same object. This can cause unexpected changes.

original = [[1, 2, 3], [4, 5, 6]]
copy = original  # ❌ Not a copy!

copy[0][0] = 99
print(original)  # [[99, 2, 3], [4, 5, 6]] — changed!
print(copy)     # [[99, 2, 3], [4, 5, 6]]

Shallow copy (.copy())

A shallow copy creates a new object, but nested objects are still shared:

import copy

original = [[1, 2, 3], [4, 5, 6]]
shallow = original.copy()  # or copy.copy(original)

# Modify nested list:
shallow[0][0] = 99

print(original)  # [[99, 2, 3], [4, 5, 6]] — changed!
print(shallow)  # [[99, 2, 3], [4, 5, 6]]

The outer list is new, but the inner lists are still the same objects.

Deep copy (deepcopy())

A deep copy creates completely independent copies of all nested objects:

import copy

original = [[1, 2, 3], [4, 5, 6]]
deep = copy.deepcopy(original)

# Modify nested list:
deep[0][0] = 99

print(original)  # [[1, 2, 3], [4, 5, 6]] — unchanged!
print(deep)     # [[99, 2, 3], [4, 5, 6]]

Now both the outer list and all nested lists are completely independent.

For simple (flat) lists

For lists without nested structures, shallow copy works fine:

original = [1, 2, 3]
copy = original.copy()  # or list(original) or original[:]

copy[0] = 99
print(original)  # [1, 2, 3] — unchanged
print(copy)      # [99, 2, 3]

When to use each

  • Shallow copy (.copy()): When nested objects are immutable (like strings, numbers) or you want to share nested data
  • Deep copy (deepcopy()): When you need completely independent copies of nested mutable objects (lists, dicts, etc.)

Other ways to copy

# For lists:
new_list = old_list.copy()      # Method
new_list = list(old_list)       # Constructor
new_list = old_list[:]          # Slicing

# For dictionaries:
new_dict = old_dict.copy()       # Method
new_dict = dict(old_dict)       # Constructor

# For nested structures:
import copy
new = copy.deepcopy(old)        # Always use deepcopy

Summary

  • Assignment (=): No copy, just another name for the same object
  • Shallow copy: New outer object, but nested objects are shared
  • Deep copy: Completely independent copy of everything