Programming Essentials~20 min

Mutability and References

A variable holds a reference to a value, not a private copy — and for a mutable value like a list, that means two names can see the same change.

By the end of this lesson, you can

  • Explain the difference between a mutable value, like a list, and an immutable one, like a number
  • Predict when two variable names refer to the same mutable object, and when changing one affects the other
  • Explain why passing a list to a function can let that function change the original, unlike passing a number

Why it matters

The very first lesson in this course established that a variable is a name that refers to a value. What it didn’t cover: some values can be changed in place, and some can’t — and for the ones that can, two different names can end up referring to the exact same one. Missing this is how a change made through one variable “mysteriously” shows up somewhere else that never seemed to touch it.

Mental model

An integer, like 5, is immutable — it can never change. “Changing” a variable that holds one really means making the name refer to a different value entirely. A list is mutable — the same list object can have its contents changed without becoming a different object.

PythonTwo names, the same mutable list
a = [1, 2, 3]
b = a
b.append(4)

print(a)
print(b)
Output
[1, 2, 3, 4]
[1, 2, 3, 4]

b = a didn’t copy the list — it made b a second name for the exact same list a already referred to. b.append(4) mutates that one shared list, so both names see the change, because there was only ever one list.

Compare that to numbers:

PythonTwo names, but numbers can't be mutated
x = 5
y = x
y = y + 1

print(x)
print(y)
Output
5
6

y = y + 1 doesn’t change the number 5 — it can’t be changed, it’s immutable. Instead, y gets reassigned to refer to the new value 6, leaving x still pointing at 5, completely unaffected.

Trace it

Shared reference (mutable) vs. independent values (immutable)
CodeWhat actually happens
a = [1, 2, 3]a refers to a new list object
b = ab refers to the same list object as a — not a copy
b.append(4)the one shared list is mutated; both a and b see [1, 2, 3, 4]
x = 5; y = xy refers to the same immutable 5 — but that's fine, since it can never change
y = y + 1y now refers to a new value, 6; x still refers to the original, unaffected 5

Functions and mutable arguments

The same rule applies when a list is passed into a function:

PythonA function that mutates its list argument
def add_item(items):
    items.append("new")

my_list = ["a", "b"]
add_item(my_list)
print(my_list)
Output
['a', 'b', 'new']

items inside the function is just another name for the same list my_list refers to outside it — items.append(...) mutates that shared list, so the caller sees the change too, even though my_list was never reassigned. A number argument behaves completely differently:

PythonReassigning a number parameter doesn't reach outside the function
def increment(n):
    n = n + 1

my_number = 5
increment(my_number)
print(my_number)
Output
5

n = n + 1 reassigns the local name n — it never touches my_number, because numbers can’t be mutated in place at all.

Check your understanding

What does this print? cart = ["apple"] backup = cart cart.append("banana") print(backup)
Or reveal the answer without checking

Answer:["apple", "banana"]
backup = cart makes backup a second name for the same list cart refers to, not a copy. Appending through cart mutates the one shared list, so backup sees "banana" too.

A function receives a list as a parameter and calls .append() on it. What happens to the caller's original list?
Or reveal the answer without checking

Answer:It changes too, since the parameter is another name for the same list object
Passing a list doesn't copy it — the parameter refers to the same list object the caller's variable does, so mutating it inside the function is visible outside too.

Practice: warm-up

Trace this program — write down what each print shows.

original = [10, 20]
copy_name = original
original.append(30)
print(copy_name)

count = 1
other_count = count
count = count + 10
print(other_count)

Stuck? Reveal one hint at a time.

  1. Hint 1

    copy_name = original does not make a copy — check what it actually does.

  2. Hint 2

    count is a number, so count = count + 10 works completely differently than original.append(30) does.

Reveal the trace

Try the problem yourself before reading this. There is often more than one reasonable approach — treat this as one worked example, not the only correct answer.

copy_name = original          -> same list, not a copy
original.append(30)           -> mutates the one shared list
print(copy_name)               -> [10, 20, 30] — sees the change

other_count = count            -> other_count refers to the same 1, but numbers can't mutate
count = count + 10              -> count now refers to a new value, 11
print(other_count)              -> 1 — completely unaffected

Practice: apply it

This function is supposed to return a sorted copy of a list without changing the original, but the original gets changed anyway:

def get_sorted(numbers):
    numbers.sort()
    return numbers

scores = [30, 10, 20]
sorted_scores = get_sorted(scores)
print(scores)
What does print(scores) show, and why?
Or reveal the answer without checking

Answer:[10, 20, 30] — changed, because numbers inside the function is the same list object as scores, and .sort() mutates it in place
numbers is just another name for the same list scores refers to — calling .sort() on it mutates that one shared list, so the caller's scores ends up sorted too, not just the returned value.

Modification challenge: fix get_sorted so it doesn’t change the caller’s original list — inside the function, make a copy first with numbers = numbers.copy() before sorting, so the sort only affects that new, independent list.

Summary

  • An immutable value (a number, a string) can never change — reassignment always makes a name refer to a new value instead.
  • A mutable value (a list) can be changed in place — the same object, with different contents.
  • b = a never copies — it makes b a second name for whatever a already refers to. For a mutable value, mutating through one name is visible through every other name pointing at the same object.
  • Passing a mutable value into a function behaves the same way: the parameter is another name for the caller’s object, so mutating it inside the function is visible outside too.