References vs Values
Why changing a variable in one place sometimes changes it in another.
Prerequisites:VariablesArrays & Lists
Story — name tags vs the things behind them
Imagine the variable xs isn't a thing, but a name tag pointing at a thing. When you write ys = xs, you don't copy what xs points at — you stick a second name tag on the same item. Two tags, one item. Mutate through one tag and both will "see" the change.
For numbers and strings (which most languages treat as immutable), you can never feel the difference: assignment looks like copying because there's nothing to mutate. For lists, dicts, objects, arrays — the kinds of things you actually edit — assignment is sharing. Knowing that is the difference between code that surprises you and code that doesn't.
Pointa says
See it — assignment, mutation, copy
Three operations to keep separate: assignment (ys = xs — share), mutation (ys.append(4) — touch the shared object), and copy (ys = xs[:] or list(xs) — duplicate the data so each name has its own). Mistake any of these and the bug shows up far from where you typed it.
✎ Sharpen your pencil
In one sentence, what does the "=" operator actually do for objects in Python?
Pointa says
Try it — predict before you read
Exercise. In Python:
a = [1, 2]
b = [a, a] # b is a list of two references to the SAME inner list
b[0].append(3)
print(b)Reveal answer
[[1, 2, 3], [1, 2, 3]]. Both inner lists in b are the same object — a single reference appearing twice. Mutate through either, both "change."
Code it — five languages, two semantics
Python, JavaScript, Java pass objects by reference (assignment shares). C++ defaults to copy unless you explicitly take a reference (&) or pointer (*). Go is more nuanced — most types are values, but slices/maps share underlying storage.
# Numbers, strings, tuples — value-like (immutable)
a = 5
b = a
b += 1
print(a, b) # 5 6 — independent
# Lists, dicts — reference-like (mutable, aliased)
xs = [1, 2, 3]
ys = xs # SAME list, two names
ys.append(4)
print(xs) # [1, 2, 3, 4] — surprised?
# To copy, copy explicitly
zs = xs[:] # or list(xs), or xs.copy()
zs.append(5)
print(xs) # [1, 2, 3, 4] — untouchedQuiz it — make it stick
No Dumb Questions
Real questions other learners asked on this page.
Why does my function modifying a list affect the caller?
Because the function received a reference to the same list. Passing a list "by value" would copy the entire list — slow and almost never what you want. To prevent mutation, copy explicitly (list[:], list.copy()) before passing.What's a deep copy?
Recursively copy every level — the outer list, the lists inside it, the dicts inside those. A shallow copy only duplicates the outer container; the inner objects stay shared. Use copy.deepcopy in Python; structuredClone in modern JS.Why are integers in Python "value-like" if everything is a reference?
Because integers are immutable. You can't mutate the number 5 — operations like x += 1 create a new int object and rebind the name. Immutability makes the reference vs value distinction invisible for those types.