Memory Allocation in Python: Assignment vs Copy vs Deepcopy
When working with collections in Python, such as lists, dictionaries, sets, and custom objects, it's crucial to understand memory allocation. By the end of this article, you'll know the difference between assignment, shallow copying, and deep copying. You'll also be able to identify their implications on memory usage, object mutability, and time complexity.
1. Assignment (=
): Reference Binding
When you assign one variable to another, Python does not duplicate the object. Instead, it simply binds another reference to the same object in memory.
a = [1, 2, 3]
b = a # ⬅️ Assignement, not a copy ⚠️
a[0] = 99
print(b) # [99, 2, 3]
Here, both a
and b
point to the same list in memory. Modifying one affects the other.
- Time complexity:
- O(1) because, only a reference is copied, not the object itself.
- Memory usage:
- Minimal, just stores another reference to the same object.
2. Shallow Copy (copy.copy()
or slicing)
A shallow copy creates a new container object but does not recursively copy nested objects. Instead, it only copies references for inner elements.
import copy
a = [[1, 2], [3, 4]]
b = copy.copy(a) # or a[:] or a.copy()
a[0][0] = 99
print(a)
# [[99, 2], [3, 4]]
print(b)
# [[99, 2], [3, 4]] ➡️ inner list still shared
# empty list object a
a.clear()
print(a)
# []
print(b)
# [[99, 2], [3, 4]] ➡️ outer list not shared
a
andb
are different outer lists,- but their elements (inner lists) still point to the same objects.
- Time complexity:
- O(n) for the outer container (it needs to iterate through all elements).
- Memory usage:
- Stores a new container object, but inner objects are shared.
3. Deep Copy (copy.deepcopy()
)
A deep copy
recursively duplicates all nested objects, ensuring that the new container and all its contents are independent.
import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)
a[0][0] = 99
print(a)
# [[99, 2], [3, 4]]
print(b)
# [[1, 2], [3, 4]] ➡️ fully independent copy
- Both outer and inner objects are new copies.
- Changes in one structure will not affect the other.
- Time complexity:
- O(n) for the number of elements at each level.
- Worst-case: O(n.m) where
m
is the average size of nested structures.
- Memory usage:
- Much higher, since every nested object is duplicated.
4. Special Case: Immutable Objects
With immutable objects (e.g., intergers, strings, tuples of immutables), copying is unnecessary. Assignment, shallow copy, and deepcopy all behave the same way, they simply reference the same object, because immutables can't be modified.
x = "apricot"
y = x
z = copy.copy(x)
w = copy.deepcopy(x)
print(x is y is z is w)
# True
- Time complexity:
- All are O(1).
- Memory usage:
- No additional allocations (Python reuses immutable objects when possible).
5. Quick Complexity Cheat Sheet
Operation | Effect | Time Complexity | Memory |
---|---|---|---|
b = a (assignment) |
New reference to the same object | O(1) | Minimal |
copy.copy(a) (shallow) |
New outer object, inner objects shared | O(n) | Moderate |
copy.deepcopy(a) (deep) |
Fully independent duplicate of all nested objects | O(n·m) (nested) | High |
Immutable assignment/copy | Always same object (no real copy) | O(1) | Minimal |
6. Benchmarking
Let’s see how these operations actually perform in practice using timeit
.
import timeit
import copy
setup_code = """
import copy
a = [[i for i in range(100)] for _ in range(1000)] # Large nested list
"""
assignment = "b = a"
shallow = "b = copy.copy(a)"
deep = "b = copy.deepcopy(a)"
print("Assignment:", timeit.timeit(assignment, setup=setup_code, number=10000))
print("Shallow copy:", timeit.timeit(shallow, setup=setup_code, number=1000))
print("Deep copy:", timeit.timeit(deep, setup=setup_code, number=10))
Example Output (on a modern laptop):
Assignment: 0.0012 seconds
Shallow copy: 0.083 seconds
Deep copy: 3.547 seconds
- Assignment is nearly instantaneous, even for large objects.
- Shallow copy is fast but scales linearly with the container size.
- Deep copy is order of magnitude slower, as it must recursively allocate and copy everything.
7. Best Practices
- ✅ Use assignement when you just need another reference.
- ✅ Use shallow copy when you only need a new container but can share nested objects.
- ✅ Use deep copy only when full independence is required and be aware of the cost.
- ✅ Benchmark if performance matters: copying deeply nested structures can easily become a bottleneck.
✅ Key takeaway:
In Python, not all copies are created equal. Understanding when you are working with references, shallow copies, or deep copies and knowing their cost is essential for writing efficient, bug-free code.
Sept. 18, 2025, 5:14 p.m.