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 is mutation and copying bug?
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.
Errors and symptoms this causes
- editing copy changes original
- list.sort() returns None
- shared state between instances
- mutable default argument
- removing items skips every other one
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.
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.