dict.copy()
Return a shallow copy of the dict. New container, same nested references — the classic shallow-vs-deep-copy trap.
Common call
backup = state.copy()
Returns
a new dict — top-level key/value pairs are independent
Replaces
`dict(d)` and `{**d}` — all three are equivalent shallow copies
Watch out
shallow only — nested lists/dicts remain shared references
dict.copy()
→ dict
Demo
Live evaluation
Try:
Inputs
dictdictkey: value pairs
Output
{'a': '1', 'b': '2', 'c': '3'}.copy()
{'a': '1', 'b': '2', 'c': '3'}
The demo shows the copied dict — for the simple values passed here (strings and numbers), the copy is effectively independent because those values are immutable. The interesting behavior lives in nested MUTABLE values (lists, dicts, sets) — mutating them in the copy also mutates the original. See pitfalls.
Common patterns
Snapshot before mutating
Keep the original for comparison or rollback.
before = state.copy() state.update(overrides) changed = {k: state[k] for k in state if state[k] != before.get(k)}
Independent per-caller copies
Give each recipient its own dict so they can mutate freely.
defaults = {"timeout": 30, "retries": 3} def make_config(): return defaults.copy()
Deep copy for nested mutables
When the values themselves need to be independent, use copy.deepcopy.
from copy import deepcopy independent = deepcopy(state)
Examples
1. Basic
d = {"a": 1}
e = d.copy()
e["b"] = 2
d
Returns
{"a": 1} # original untouched2. Empty
{}.copy()
Returns
{}3. Equivalent forms
d.copy() == dict(d) == {**d}
Returns
True # all three are shallow copies4. Shared nested
d = {"n": [1, 2]}
e = d.copy()
e["n"].append(3)
d["n"]
Returns
[1, 2, 3] # nested list is shared!5. Independent top level
d = {"a": 1}
e = d.copy()
e["a"] = 99
d["a"]
Returns
1 # top-level key rebound only in the copyPitfalls
1. Shallow only — nested mutables are SHARED
The most-copied confusion around copy(). A nested list or dict lives at one memory address; both the original and the copy point to it. Mutating that nested object shows up on both sides.
Nested change leaks
orig = {"tags": ["a", "b"]} backup = orig.copy() backup["tags"].append("c") orig["tags"]
["a", "b", "c"] # original also has "c"
Use deepcopy
from copy import deepcopy backup = deepcopy(orig) backup["tags"].append("c") orig["tags"]
["a", "b"] # original safe
2. copy() vs the `=` assignment
Assignment does NOT copy — it makes another name for the same dict. Mutating either name mutates the same object. A common bug when passing dicts between functions.
Alias, not copy
d = {"a": 1} e = d e["b"] = 2 d
{"a": 1, "b": 2} # d changed too
Explicit copy
d = {"a": 1} e = d.copy() e["b"] = 2 d
{"a": 1} # d untouched
3. Three ways to shallow-copy — pick one for consistency
`d.copy()`, `dict(d)`, and `{**d}` all produce shallow copies. They differ in the reader's intent and in some subclass edge cases: dict(d) always returns a plain dict; d.copy() and {**d} may preserve subclass type in most cases.
Random mix
a = d.copy() b = dict(d) c = {**d} # same result, three styles
inconsistent house style
Pick one
copy_of_d = d.copy() # or {**d}, whichever your style guide picks
consistent
4. Shared references also affect view objects
A shallow copy shares the values but has its own view objects. Iterating the ORIGINAL's items sees updates the copy makes to nested mutables — not because the views are shared, but because the values are.
Not the views
d = {"n": []} e = d.copy() e["n"].append(1) list(d.values())
[[1]] # d's value list saw the change
Deep for independence
e = deepcopy(d)
no leakage
When to use
Use it
- Snapshotting a dict before mutating for rollback or diff
- Giving each caller its own dict to mutate freely
- Any "same shape, different object" workflow with immutable values
- Composing with update() to layer overrides without touching the source
Reach for something else
- Nested mutable values need to be independent → copy.deepcopy
- Rebinding is enough — you never mutate → `=` assignment is cheaper
- Duplicating for read-only iteration only → no copy needed at all
- Large dicts where the shallow copy is a hot-path allocation
Notes
Complexity
O(n) — one pass over the key-value pairs
Return
A new dict of the same type; values are the same object references
CPython impl
Objects/dictobject.c :: dict_copy
Memory
Allocates a new dict; values are not deep-copied
Thread-safe
The copy operation is safe against concurrent reads; not safe under concurrent writes
FAQ
copy() duplicates the dict itself but keeps the same VALUE references. Nested lists / dicts / sets are shared. deepcopy() (from the copy module) recursively duplicates every level — the copy is fully independent, at the cost of more time and memory.
History
1.0
copy() has been part of dict since Python 1.0.