Difference Between Deep Copy and Shallow Copy in Python
Learn the real difference between deep copy and shallow copy in Python, how nested mutable objects are shared or duplicated, with code examples.
Expected Interview Answer
A shallow copy creates a new outer container but fills it with references to the same nested objects as the original, so mutating a nested object affects both copies, while a deep copy recursively duplicates every nested object too, producing a fully independent structure with no shared references at any level.
Python's copy module provides copy.copy() for shallow copies and copy.deepcopy() for deep copies; many built-in types also expose shallow-copy shortcuts like list(original), original[:], or original.copy(). For a flat structure containing only immutable values (ints, strings), shallow and deep copies behave identically because there is nothing nested to share. The difference only shows up with nested mutable containers — a list of lists, or a dict containing lists — where a shallow copy still lets changes to an inner list leak into the 'copy'. deepcopy() handles circular references correctly by tracking already-copied objects in a memo dictionary, but it is slower and uses more memory since it recursively walks and duplicates the whole object graph. Custom classes can control deep-copy behavior by implementing __deepcopy__.
- Shallow copy is cheap and correct when nested data is immutable or shared intentionally
- Deep copy guarantees full independence between original and copy
- copy.deepcopy() safely handles circular references via a memo dict
- Built-in shortcuts (list[:], dict.copy()) make shallow copies fast and idiomatic
- Classes can customize behavior via __copy__ and __deepcopy__
AI Mentor Explanation
A shallow copy is like photocopying a team sheet but leaving the actual players unchanged — the new sheet lists the same real players, so if a player gets injured, both sheets now show that same injured player. A deep copy is like cloning the entire squad, including fresh, independent copies of every player, so injuring a clone on one sheet never affects the original squad at all.
Step-by-Step Explanation
Step 1
Shallow copy definition
Creates a new outer container, but its elements are references to the same nested objects as the original.
Step 2
Deep copy definition
Recursively duplicates the outer container and every nested object, producing full independence.
Step 3
When they behave the same
For structures containing only immutable values, shallow and deep copies produce identical practical behavior.
Step 4
Where they diverge
With nested mutable containers (list of lists, dict of lists), mutating a nested object via a shallow copy leaks into the original.
Step 5
API to use
copy.copy() for shallow, copy.deepcopy() for deep; also list.copy(), dict.copy(), or slicing [:] for shallow shortcuts.
Step 6
Circular references
deepcopy() uses an internal memo dict to detect already-copied objects and avoid infinite recursion.
What Interviewer Expects
- Clearly distinguishes 'copies references' vs 'copies recursively'
- Gives a concrete example where a shallow copy causes a bug via shared nested state
- Knows the copy module API: copy.copy() and copy.deepcopy()
- Mentions that deep copy is slower/more memory-intensive
- Knows deepcopy handles circular references via a memo dict
Common Mistakes
- Assuming list(original) or original[:] performs a deep copy
- Not realizing a shallow copy is fine for flat, immutable-only data
- Forgetting custom classes need __deepcopy__ for correct deep-copy behavior
- Believing deepcopy() will infinite-loop on circular references (it doesn't, due to memoization)
Best Answer (HR Friendly)
“A shallow copy makes a new container but still shares the inner contents with the original, so changing something nested affects both. A deep copy makes a fully independent duplicate, including everything nested inside, so the two copies can never affect each other after that.”
Code Example
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
shallow[0].append(99) # mutate a nested list
deep[0].append(-1)
print(original) # [[1, 2, 99], [3, 4]] <- shallow copy leaked into original
print(shallow) # [[1, 2, 99], [3, 4]]
print(deep) # [[1, 2, -1], [3, 4]] <- fully independentFollow-up Questions
- How does copy.deepcopy() avoid infinite loops with circular references?
- How would you implement __deepcopy__ for a custom class?
- Is slicing a list ([:]) a shallow or deep copy?
- What is the performance cost difference between shallow and deep copy?
- How does copying a dict of lists differ from copying a flat dict?
MCQ Practice
1. In a shallow copy of a list of lists, what happens if you append to a nested list in the copy?
A shallow copy shares references to nested objects, so mutating a nested list is visible through both the original and the copy.
2. Which function performs a full recursive copy in Python?
copy.deepcopy() recursively duplicates the object and all nested objects, producing a fully independent structure.
3. How does deepcopy() handle an object graph with circular references?
deepcopy() maintains an internal memo dictionary mapping already-copied object ids to their copies, safely handling cycles.
Flash Cards
What does a shallow copy share with the original? — References to the same nested mutable objects.
What does a deep copy do differently? — Recursively duplicates every nested object for full independence.
Which module provides copy() and deepcopy()? — The built-in copy module.
How does deepcopy avoid infinite loops on circular refs? — It uses a memo dict tracking already-copied objects by id.