copy.deepcopy
Walks the whole object graph. A memo dict maps each original object to its copy, so an object referenced twice is copied once — and a structure that contains itself is copied without infinite recursion.
Demo
import copy original = [[1, 2], [3]] d = copy.deepcopy(original) d[0].append(0) d.append([]) (original, d)
The original stays [[1, 2], [3]] whatever you do to the deep copy. The cycle demo prints [1, 2, [...]] — the [...] is how repr shows a list inside itself — and (True, False) confirms the copy points at itself, not at the original. In the shared demo appending 3 to d[0] shows up in d[1] as well, because the memo made both slots refer to the same new list, while outer is untouched.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| obj | object | yes | Anything copyable. |
| memo | dict | no (None) | id(original) → copy for objects already copied in this pass. Leave it out; pass it on only inside your own __deepcopy__(self, memo). |
Return value
same type as obj — A fully independent copy (immutable parts may be the same objects).
Common patterns
import copy history.append(copy.deepcopy(board)) apply_move(board, move)
import copy class Node: def __deepcopy__(self, memo): new = Node.__new__(Node) memo[id(self)] = new new.children = copy.deepcopy(self.children, memo) new.parent = copy.deepcopy(self.parent, memo) return new
rows_copy = [row.copy() for row in rows]
Examples
Pitfalls
import copy matrix = [[1, 2], [3, 4]] m2 = copy.copy(matrix) m2[1][1] = 0 matrix
import copy matrix = [[1, 2], [3, 4]] m2 = copy.deepcopy(matrix) m2[1][1] = 0 matrix
import copy, threading class Service: def __init__(self): self.data = [1] self.lock = threading.Lock() copy.deepcopy(Service())
import copy, threading class Service: def __init__(self): self.data = [1] self.lock = threading.Lock() def __deepcopy__(self, memo): new = Service() new.data = copy.deepcopy(self.data, memo) return new copy.deepcopy(Service()).data
When to use
- Nested mutable data that must be fully independent
- Undo stacks, simulations, test fixtures built from a template
- Graphs with shared nodes or cycles
- Flat containers → .copy() is enough and much faster
- JSON-like data on a hot path → a hand-written copy of the parts you change
- Objects with OS resources → write __deepcopy__ or copy the data only
Notes
FAQ
copy.deepcopy(d). dict.copy() and dict(d) are shallow: nested lists and dicts inside are still shared with the original.