Shared references: when changing a copy changes the original
Assignment does not copy, a shallow copy still shares its contents, and mutating methods return None.
What are mutation and copying bugs?
Two names refer to the same underlying object, so a change through one is visible through the other. Or a method mutates in place and returns nothing, and the nothing gets assigned.
Why it happens
Assignment binds a name; it never copies. A shallow copy duplicates the outer container while its elements still point at the originals, so mutating a nested value is visible through both. Python evaluates default arguments once at definition, so a mutable default is shared by every call. And methods that mutate in place return None by convention, which is easy to assign by accident.
How to recognise it
- A function that should be read-only changes its argument.
- Two fresh objects share state.
- A variable becomes None after a sort or reverse.
- A list changes length while you iterate it, and elements get skipped.
Error guides caused by this pattern
All error guides →In-depth guides for common exceptions and runtime errors triggered by mutation and copying bugs, with working fixes and real-world examples:
How to fix it
Copy explicitly, and match the copy's depth to the depth at which you mutate — a shallow copy protects only the top level. Use None as a default and build the mutable value inside the function. Remember which methods return a new value and which return None: that distinction marks the ones that mutate.
Breaks vs Works Example
Breaks
# Python: mutable default argument shared across all invocations
def add_tag(tag, tags=[]):
tags.append(tag)
return tagsWorks
# Python: use None as sentinel default and initialize inside function
def add_tag(tag, tags=None):
if tags is None:
tags = []
tags.append(tag)
return tags💡 Python default parameters are evaluated once when the function is defined, sharing state between calls.
Practise mutation and copying bugs
Working code with one bug in it. Find it, fix it in the browser, and see the explanation. No account needed.
Stuck on your own code?
Paste it into the visualizer and watch it run line by line, with every variable at every step. Free, and it runs in your browser.