Shallow and deep copies
A copy can be one level deep (the outer container is new, everything inside is shared) or all the way down. Choosing the wrong one leaks changes into data you meant to keep.
- Name the shallow copies (
copy.copy,list(),dict(), slices,.copy()) and what they share - Use
copy.deepcopyfor independent nested data, and know it preserves shared parts and cycles - Decide when a shallow copy is enough and when a deep copy is needed
Every common way of copying a container is shallow: copy.copy(x), list(x), x[:], x.copy(), dict(x). It creates a new outer container whose slots refer to the same inner objects. Changing the copy's top level (adding, removing, replacing an entry) is safe; changing an object inside it changes the original's too.
copy.deepcopy(x) copies recursively, so nothing mutable is shared with the original.
Run each example to see what it prints. Running examples doesn't count toward progress.
import copy template = {"plan": "standard", "limits": {"users": 5, "storage_gb": 10}} shallow = copy.copy(template) deep = copy.deepcopy(template) shallow["plan"] = "pro" shallow["limits"]["users"] = 50 print(template) print(deep["limits"]["users"], shallow["limits"] is template["limits"], deep["limits"] is template["limits"])
deepcopy is smarter than *copy everything*: it remembers what it has already copied (a memo), so an object referenced twice in the original is copied once and referenced twice in the copy, and a structure that refers to itself (a cycle) doesn't send it round forever.
import copy address = {"city": "Houston", "state": "TX"} customer = {"id": "C00077", "billing": address, "shipping": address} clone = copy.deepcopy(customer) clone["shipping"]["city"] = "Austin" print(clone["billing"]["city"], customer["billing"]["city"]) print(clone["billing"] is clone["shipping"], clone["billing"] is address) node = {"name": "loop"} node["next"] = node twin = copy.deepcopy(node) print(twin["next"] is twin, twin is node)
When to use which: a shallow copy when you only add, remove or replace top-level entries (sorting a copy of a list of records, filtering a dictionary). A deep copy when you will change something nested and the original must stay as it was (a template, a saved snapshot, test fixtures). Immutable values (numbers, strings, tuples of immutables) are never a problem: they can't change, so sharing them is free. Mistakes to avoid: using list(rows) or dict(settings) and then editing inside them; and deep-copying large data out of habit, which costs time and memory for every object.
copy.copy,list(),dict(), slices and.copy()are shallow: a new outer container, shared insides.copy.deepcopycopies every level, keeps doubly referenced parts shared once, and handles cycles.- Shallow is enough for top-level changes; deep is needed before changing something nested.
- Next: objects that refer to each other in a cycle, which reference counting alone never frees.