Course outline · 0% complete

0/32 lessons0%

Course overview →

Two Names, One List

lesson 6-4 · ~9 min · 20/32

Assignment copies the name, not the list

b = a does NOT copy a list. This follows from what assignment has meant since lesson 1-2: it attaches a name to a value, it never duplicates the value. After b = a, both names point to the same list in memory, so a change made through either name is visible through both.

You never noticed this with ints and strings because they are immutable, there is nothing a second name could change. Lists are mutable, so a shared list can be edited out from under you, and that is a classic real-world bug: a second variable, or a function you passed the list to, "mysteriously" modifies your data.

When you genuinely want an independent copy, build one: list(a) (or the slice a[:]) creates a new list containing the same items.

ab[1, 2, 3, 4]b = a : one list, two namesc[1, 2, 3, 99]c = list(a) : a new listassignment shares, list() copies
b = a points a second name at the same list. list(a) builds a genuinely separate list that can change independently.

Sharing against copying, side by side

Predict all three printed lists before reading the output. b shares a's list while c is a real copy.

a = [1, 2, 3]
b = a
b.append(4)
print(a)
c = list(a)
c.append(99)
print(a)
print(c)

Output

[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4, 99]

The first print already shows the surprise. Nothing was appended through a, yet a has four items, because b.append(4) modified the single list that both names refer to.

The second and third prints show the contrast. list(a) built a genuinely separate list, so appending 99 through c left a untouched at four items while c has five. The two lists now have independent futures, which is exactly what a backup or a working copy requires.

This snippet prints [1, 2, 3].

x = [1, 2]
y = x
y.append(3)
print(x)

The line y = x did not copy anything. It made y a second name for the same list, so appending through y modified the one list both names refer to, and x sees the change.

Keeping x untouched requires an actual copy, written y = list(x) or y = x[:]. The reason this bug is dangerous in real code is that it is invisible at the point of the mistake: the append looks local and correct, and the damage only surfaces later when some other part of the program reads x and finds data it never modified.

Making a real backup means copying before the change, and using list() rather than a plain assignment.

original = ["read", "code", "sleep"]
backup = list(original)
original.append("gym")
print(original)
print(backup)

Output

['read', 'code', 'sleep', 'gym']
['read', 'code', 'sleep']

The two prints differ, which is the proof that the copy is independent. list(original) built a new list holding the same three items, so the later append reached only the original.

Both parts of the setup matter. Writing backup = original would share instead of copy and both lines would show gym, and copying after the append would preserve the wrong state. A backup that shows the change it was meant to guard against is the signature of having copied the name rather than the list.