shallow copy, deep copy, and the nested-dict bug
A copy is a new container with the same items
Last lesson made the rule clear: b = a doesn't copy. To get a real
separate list, you have to create a new one. This lesson nails the
"how" — three idiomatic ways AI uses to copy a list, and then the
fine print that bites later.
The three ways to copy a flat list
All three of these allocate a new list and fill it with the items from the original:
a = original[:] # slice from start to end
b = list(original) # constructor
c = original.copy() # method
Read them as:
original[:]— slice notation with no start or end means every item, beginning to end. Slicing always returns a new list. This is the oldest idiom and the shortest.list(original)— thelist()constructor takes any iterable and builds a new list from it. Works for tuples, sets, generators, anything that yields values.original.copy()— added to lists in Python 3.3, this is the most readable for new code. The method explicitly says "make a copy."
Pick the one that fits. They produce identical results for lists.
A worked example
The editor on the right makes three copies, then mutates one of them:
original = [1, 2, 3]
a = original[:]
b = list(original)
c = original.copy()
a.append(99)
print("original:", original)
print("a:", a)
Output:
original: [1, 2, 3]
a: [1, 2, 3, 99]
a got the 99. original did not. The copy worked — they're now
two separate lists, and mutating one doesn't affect the other.
(The same is true for b and c, which we just don't print here.)
What "shallow copy" actually means
Each of these three techniques produces what's called a shallow copy: a new outer list, with the same items inside.
For a flat list of numbers — which is what original is here — that
distinction doesn't matter. Numbers and strings in Python are
immutable, meaning you can't mutate the 1 itself; you can only
remove it from the list and put a different number in. So "same
items inside" causes no problems.
But the moment your list contains mutable items — nested lists, dicts, sets, custom objects — the "same items inside" part becomes a trap. You have a new outer list, but the inner contents are still shared between the original and the copy. Mutate something nested, and the change shows up in both.
That's the bug we'll see in the next lesson. For now, hold this thought: shallow copies are fine for flat data, dangerous for nested data.
Where AI specifically reaches for each
Cursor's habits, from reading a lot of AI-generated code:
.copy()— most common in modern code. Reads cleanly.list(...)— common when converting from a different type (tuple, set, generator) to a list, and getting a copy at the same time.[:]— common in older code or when AI is in "compact" mode. Newer code uses.copy()instead.
All three are correct. When you read AI code that does any of them, recognize it as "make a real copy" and move on.
The rule of thumb to lock in
For flat data, shallow copies (
[:],list(x),x.copy()) are fine. For nested data, you needcopy.deepcopy(x)from thecopymodule.
We'll cover the nested case next. Run the editor for now and see
that the simple flat copy works exactly as advertised — a got the
99, original did not.