Using Copy Module
Pythonimport copy # Shallow copy: new outer object, but nested objects are shared references shallow = copy.copy(original) # Deep copy: new outer object AND recursively copies all nested objects deep = copy.deepcopy(original)
-
Identify the problem: Are you seeing unexpected mutations propagate between two variables that should be independent? This is an aliasing bug — assignment (
b = a) does not copy, it just binds a new name to the same object. -
Decide copy depth needed:
- Flat structure (no nested mutables) →
copy.copy()is sufficient and cheaper. - Nested structures (list of lists, dict of objects, etc.) → use
copy.deepcopy().
- Flat structure (no nested mutables) →
-
Check for built-in shortcuts first (often faster/clearer than importing
copy):list(original)ororiginal[:]for shallow list copydict(original)for shallow dict copyoriginal.copy()— most built-in containers have this method
-
For custom classes, decide if default behavior is enough:
- Default
copy.copy()/copy.deepcopy()copies the__dict__(shallow/deep respectively). - Override
__copy__(self)and/or__deepcopy__(self, memo)when the class manages external resources (file handles, locks, sockets) that shouldn't be blindly duplicated.
- Default
-
Handle circular references:
deepcopyautomatically tracks already-copied objects via thememodict to avoid infinite recursion — no extra work needed unless implementing__deepcopy__yourself, in which case passmemothrough to recursivecopy.deepcopy(x, memo)calls. -
Test the copy: mutate the original after copying and confirm the copy is unaffected (for deepcopy) or that only top-level independence holds (for shallow copy).
Example 1: Shallow copy pitfall Input:
Pythonimport copy original = [[1, 2], [3, 4]] shallow = copy.copy(original) shallow[0].append(99) print(original)
Output:
Python[[1, 2, 99], [3, 4]] # inner list mutated because it's shared by reference
Example 2: Deep copy fixes it Input:
Pythonimport copy original = [[1, 2], [3, 4]] deep = copy.deepcopy(original) deep[0].append(99) print(original)
Output:
Python[[1, 2], [3, 4]] # unaffected, inner lists were fully duplicated
Example 3: Custom __deepcopy__
Input:
Pythonimport copy class Connection: def __init__(self, socket): self.socket = socket # unpicklable/unshareable resource def __deepcopy__(self, memo): # Don't duplicate the actual socket; share the reference new = Connection.__new__(Connection) memo[id(self)] = new new.socket = self.socket return new conn2 = copy.deepcopy(conn1)
Output:
Python# conn2 is a new Connection object, but conn2.socket is conn1.socket (same object)
- Prefer built-in
.copy(), slicing, or constructor calls (list(),dict(),set()) overcopy.copy()for simple containers — they're idiomatic and slightly faster. - Use
copy.deepcopy()whenever objects contain nested mutable structures and true independence is required. - When implementing
__deepcopy__, always accept and thread through thememodictionary to correctly handle shared/circular references. - For classes holding non-copyable resources (files, sockets, locks, threads), implement
__copy__/__deepcopy__explicitly rather than relying on defaults. - Remember atomic/immutable types (int, str, tuple of immutables, frozenset) are returned as-is by
copy/deepcopy— no need to copy them manually. - Use
copy.deepcopy(obj, memo={})manually only in rare cases where you need to pre-seed the memo (e.g., to force sharing of a specific sub-object).
- Assuming
b = acopies: it doesn't — both names reference the identical object. - Using shallow copy on nested structures: leads to subtle bugs where mutating a "copy" affects the original's nested data.
- Forgetting
__deepcopy__needsmemo: skipping it breaks cycle detection and can cause infinite recursion or duplicate objects that should be shared. - Deep-copying unpicklable/unshareable resources by default: sockets, file handles, and thread locks generally cannot (and should not) be deep-copied — override
__deepcopy__to handle them explicitly. - Overusing
deepcopyfor performance-critical code: it's recursive and can be slow on large or deeply nested structures; use shallow copy or restructure data when full independence isn't needed.