Shallow Copy vs Deep Copy in Python Explained
In Python, duplicating an object is primarily achieved through either a shallow copy or a deep copy, and understanding the operational difference between the two is vital for preventing unexpected bugs when mutating data. While both approaches generate a new outer container object, they diverge fundamentally in how they treat nested, mutable elements. A shallow copy inserts references to the original nested objects into the new container, meaning modifications to inner items affect both instances. Conversely, a deep copy recursively duplicates every object encountered within the original structure, creating an entirely independent clone. This guide covers the mechanics of each copying method, their operational differences, and practical examples of their behavior.
The Foundation: Assignment vs. Copying
Before comparing shallow and deep copies, it is essential to
distinguish them from standard variable assignment (=).
Assigning a variable does not create a new object; it merely creates a
new reference (or alias) bound to the existing object in memory:
original = [1, [2, 3]]
alias = original # Both point to the exact same memory address
alias.append(4)
print(original) # Output: [1, [2, 3], 4]To create an actual independent container rather than a reference,
Python provides the built-in copy module.
What is a Shallow Copy?
A shallow copy creates a new collection object, but populates it with references to the child objects contained in the original.
How to Create a Shallow Copy
Shallow copies can be constructed using:
- The
copy()function from thecopymodule:copy.copy(x) - Built-in object methods, such as
list.copy()ordict.copy() - Slice notation on sequences:
x[:] - Type constructors:
list(x),dict(x),set(x)
Operational Behavior
Because a shallow copy creates a new container, adding or removing elements from the top-level container does not impact the original. However, because child objects are copied by reference, mutating a nested mutable item alters that item across both the original and the copied structure.
import copy
original = [1, [2, 3]]
shallow = copy.copy(original)
# Modifying the top-level list
shallow.append(4)
print(original) # Output: [1, [2, 3]] (unaffected)
print(shallow) # Output: [1, [2, 3], 4]
# Modifying a nested object
shallow[1].append(99)
print(original) # Output: [1, [2, 3, 99]] (affected!)
print(shallow) # Output: [1, [2, 3, 99], 4]What is a Deep Copy?
A deep copy constructs a new collection object and then recursively copies all nested objects found inside it.
How to Create a Deep Copy
A deep copy is created using:
- The
deepcopy()function from thecopymodule:copy.deepcopy(x)
Operational Behavior
Because deep copying operates recursively, it walks the entire object graph. Every mutable child, grandchild, and nested element is duplicated at a new memory address. Consequently, changes made anywhere inside the deep-copied structure—regardless of nesting depth—will never reflect in the original object.
import copy
original = [1, [2, 3]]
deep = copy.deepcopy(original)
# Modifying a nested object
deep[1].append(99)
print(original) # Output: [1, [2, 3]] (completely isolated)
print(deep) # Output: [1, [2, 3, 99]]Key Operational Differences
| Feature | Shallow Copy (copy.copy) |
Deep Copy
(copy.deepcopy) |
|---|---|---|
| Recursion | Copies only the immediate parent container. | Recursively copies all child and nested objects. |
| Nested Object Identity | Shares memory addresses
(id()) of nested items. |
Generates new memory addresses
(id()) for nested items. |
| Side Effects | Mutating nested objects affects the original. | Fully isolated; no side effects on the original. |
| Performance | Fast execution; minimal memory overhead. | Slower execution; higher memory consumption. |
| Cyclic References | Not applicable (does not traverse children). | Automatically tracked via an internal memo dictionary to prevent infinite loops. |
When to Use Each
- Use a Shallow Copy when dealing with flat data structures (lists or dictionaries containing only immutable types like integers, strings, or tuples), or when nested objects are intentionally meant to be shared across containers to save memory.
- Use a Deep Copy when dealing with complex, nested data structures (such as trees, graphs, or multi-dimensional matrices) where modifications to the duplicate must remain strictly isolated from the original data.