Mastery
Mastery/Python/A. Object model, identity & memory
T1 · high-leverage

Shallow vs deep copy

copy.copy() duplicates the outer container only — nested mutable objects are still shared references. copy.deepcopy() recursively duplicates everything. Slicing (lst[:]) and list(lst) are also shallow copies, which surprises people who expect them to fully isolate nested data.

python
import copy

original = {"name": "a", "tags": ["x", "y"]}

shallow = copy.copy(original)
deep = copy.deepcopy(original)

shallow["tags"].append("SHALLOW-MUTATED")

print("original:", original)
print("shallow:  ", shallow, " <- same dict copied, but 'tags' list is the SAME object as original's")
print("deep:     ", deep, " <- untouched, fully independent")

print()
print("shallow['tags'] is original['tags']:", shallow["tags"] is original["tags"])
print("deep['tags'] is original['tags']:   ", deep["tags"] is original["tags"])

Custom classes can control this via __copy__/__deepcopy__:

python
class Box:
    def __init__(self, items):
        self.items = items
    def __deepcopy__(self, memo):
        print("  (custom __deepcopy__ called)")
        return Box(copy.deepcopy(self.items, memo))
    def __repr__(self):
        return f"Box({self.items})"

b = Box([1, 2, 3])
b2 = copy.deepcopy(b)
print(b, b2, b.items is b2.items)

Interview angle

Comes up naturally in questions about caching, memoization, or writing pure functions, and it's a good filter question because a shallow copy() of a list-of-lists is a silent bug — nothing raises, the program just quietly shares state it shouldn't. Interviewers use it to check whether a candidate reflexively reaches for the correct copy depth for nested data, not just whether they can recite the two function names.

In the industry

Deep copies are used deliberately but sparingly, since they're O(total object size) and can be genuinely expensive on large nested structures — which is part of why performance-sensitive code often prefers immutable data (tuples, frozen dataclasses) to sidestep the shallow-vs-deep question entirely rather than paying for deep copies defensively. Libraries that hand back internal state (ORMs, caching layers) usually document explicitly whether a returned value is a copy or a shared reference, because getting this wrong — in either direction — is a recurring, hard-to-trace class of production bug.