If you're coming to Python from another language (except one that's a lot like Python, like Ruby), and insist on understanding it in terms of that other language, here's where people usually get confused:
>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!
In Python, assignment is not mutation in Python.
In C++, if you write a = 2
, you're calling a.operator=(2)
, which will mutate the object stored in a
. (And if there was no object stored in a
, that's an error.)
In Python, a = 2
does nothing to whatever was stored in a
; it just means that 2
is now stored in a
instead. (And if there was no object stored in a
, that's fine.)
Ultimately, this is part of an even deeper distinction.
A variable in a language like C++ is a typed location in memory. If a
is an int
, that means it's 4 bytes somewhere that the compiler knows is supposed to be interpreted as an int
. So, when you do a = 2
, it changes what's stored in those 4 bytes of memory from 0, 0, 0, 1
to 0, 0, 0, 2
. If there's another int variable somewhere else, it has its own 4 bytes.
A variable in a language like Python is a name for an object that has a life of its own. There's an object for the number 1
, and another object for the number 2
. And a
isn't 4 bytes of memory that are represented as an int
, it's just a name that points at the 1
object. It doesn't make sense for a = 2
to turn the number 1 into the number 2 (that would give any Python programmer way too much power to change the fundamental workings of the universe); what it does instead is just make a
forget the 1
object and point at the 2
object instead.
So, if assignment isn't a mutation, what is a mutation?
a.append(b)
. (Note that these methods almost always return None
). Immutable types do not have any such methods, mutable types usually do.a.spam = b
or a[0] = b
. Immutable types do not allow assignment to attributes or elements, mutable types usually allow one or the other.a += b
, sometimes not. Mutable types usually mutate the value; immutable types never do, and give you a copy instead (they calculate a + b
, then assign the result to a
).But if assignment isn't mutation, how is assigning to part of the object mutation? That's where it gets tricky. a[0] = b
does not mutate a[0]
(again, unlike C++), but it does mutate a
(unlike C++, except indirectly).
All of this is why it's probably better not to try to put Python's semantics in terms of a language you're used to, and instead learn Python's semantics on their own terms.