copy() vs deepcopy()
copy() vs deepcopy()
Section titled βcopy() vs deepcopy()βWhat it is
Section titled βWhat it isβcopy.copy() makes a shallow copy: a new outer object, but nested objects inside it are still shared with the original. copy.deepcopy() makes a deep copy: the outer object and everything nested inside it are recursively duplicated, so nothing is shared.
Before this feature
Section titled βBefore this featureβWithout the copy module, βcopyingβ a list with list(original) or slicing original[:] only copies one level β nested mutable objects are still shared, which surprises people expecting a fully independent copy:
original = [[1, 2], [3, 4]]shallow = list(original) # new outer list...shallow[0].append(99) # ...but the inner lists are the SAME objectsprint(original) # [[1, 2, 99], [3, 4]] -- original was mutated too!After this feature
Section titled βAfter this featureβimport copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)shallow[0].append(99)print(original) # [[1, 2, 99], [3, 4]] -- still shared, same as list(original)
deep = copy.deepcopy(original)deep[0].append(100)print(original) # [[1, 2, 99], [3, 4]] -- unaffected, fully independent# The distinction matters most with nested mutable structures / custom objectsclass Node: def __init__(self, value, children=None): self.value = value self.children = children or []
root = Node(1, [Node(2), Node(3)])shallow_root = copy.copy(root)shallow_root.children.append(Node(4))print(len(root.children)) # 3 -- the shared `children` list was mutated too
deep_root = copy.deepcopy(root)deep_root.children.append(Node(5))print(len(root.children)) # 3 (unchanged by the deepcopy's append) -- vs 3 above for shallowWhy this is better
Section titled βWhy this is betterβcopy()is fast and often enough: for a flat structure (list of numbers, dict of strings), shallow and deep copies behave identically but shallow is cheaper.deepcopy()guarantees full independence: safe when you need to mutate a copy without any risk of touching the original, at any nesting depth.- Both are more explicit than manual copying:
copy.deepcopy(x)states intent clearly, versus ad-hoc nested-comprehension copying thatβs easy to get subtly wrong.
Key notes / edge cases
Section titled βKey notes / edge casesβ- For a flat (one-level) list/dict of immutable values (ints, strings),
copy(),deepcopy(), andlist(x)/dict(x)all behave the same β the distinction only matters once thereβs nested mutable data. deepcopy()handles circular references correctly (it tracks already-copied objects), sodeepcopy()-ing a structure that references itself wonβt infinite-loop.deepcopy()is meaningfully slower thancopy()for large nested structures β donβt reach for it by default if a shallow copy is provably sufficient.- Assignment (
b = a) copies nothing β itβs a second name for the same object. Thatβs a separate, more fundamental gotcha than shallow-vs-deep.
Quick practice
Section titled βQuick practiceβ-
After
shallow = copy.copy(original), iforiginalcontains nested lists, are those nested lists shared or independent?Answer
Shared β `copy()` only duplicates the outer container; nested mutable objects remain the same objects in both. -
When do
copy()anddeepcopy()produce the same practical result?Answer
When the structure is flat and contains only immutable values β there's nothing nested to share or duplicate differently. -
Does
deepcopy()break on a structure that contains a reference to itself?Answer
No β `deepcopy()` tracks objects it has already copied and reuses the copy, so circular references are handled safely rather than causing infinite recursion.