Immutability
16 min
An immutable value can't be changed after creation — Python's str,
int, float, tuple, and frozenset are all immutable; list,
dict, and set are mutable. Immutability is a big part of what makes
pure functions possible: if a value literally CAN'T be changed, no
function can accidentally mutate it out from under you.
t = (1, 2, 3)
t[0] = 99 # TypeError: 'tuple' object does not support item assignment
# instead, build a new tuple:
t = (99,) + t[1:]
Dicts can't be dict keys (they're mutable, so not hashable) -- freezing one into a sorted tuple of pairs is the standard way to make a dict-like value usable as a cache key.
The general pattern for "modifying" an immutable value is always the same: build a NEW value that looks like the old one with your one change applied, rather than mutating in place:
def update_immutable(t, index, value):
return t[:index] + (value,) + t[index + 1:]
print(update_immutable((1, 2, 3), 1, 99)) # (1, 99, 3) -- a new tuple
This costs more memory than mutating in place would (you're allocating a new object every time) — that's the real tradeoff immutability makes: safety and predictability, in exchange for some extra allocation. For small, frequently-shared values (config, cache keys, function arguments you don't want callers accidentally mutating), that tradeoff is usually well worth it.
Write `freeze_config(d)`: convert a dict `d` into an immutable, hashable representation — a tuple of its `(key, value)` pairs, sorted by key.
Write `update_immutable(t, index, value)`: given a tuple `t`, return a NEW tuple with the element at `index` replaced by `value` — tuples can't be mutated in place, so build the replacement via slicing.
Why can't you do `my_tuple[0] = 5` in Python?
You can convert a mutable dict into an immutable, hashable tuple representation, and "modify" an immutable tuple by constructing a new one.