Mutable & Immutable
The family tree sorted values by what kind they are. There is a second way to sort exactly the same values, and it asks a different question: once a value has been made, can it be changed? Some can. Most cannot. Python is strict about it, and it will tell you so in plain words.
1The whole idea in one question
Mutable means “can be changed”. Immutable means “cannot be changed”. (The im- at the front flips the meaning, exactly like possible and impossible.)
Careful, though — changed means something precise here. It means changed in place: the value that already exists is edited, and it is still the same value afterwards, just different. It does not mean making a new value that looks similar.
Once it is written, it is written. You cannot rub out one letter. If you want it different, you write a fresh copy on a fresh page — the original is untouched.
Rub out one word, write another, and it is the same board. The thing itself changed. Nothing new was made.
2Do not take my word for it — ask Python
Here is a fair test, and you already have the tool for it. In the last lesson you learnt indexing — [0] reaches the first item of any sequence. So far you have only used it to read an item. Now try to write to it:
word = 'hello'
print(word[0]) # reading position 0 — always fine
word[0] = 'H' # writing to position 0 — is it allowed?h
Traceback (most recent call last):
File "immutable.py", line 4, in <module>
word[0] = 'H' # writing to position 0 — is it allowed?
~~~~^^^
TypeError: 'str' object does not support item assignmentThat is the experiment. Store a value in a variable, send it the same instruction — change the item at position 0 — and watch who obeys and who refuses. Three types can be indexed, so three types can be asked: str, tuple and list.
Traceback (most recent call last):
File "try_string.py", line 2, in <module>
name[0] = 'R'
~~~~^^^
TypeError: 'str' object does not support item assignmentstr🔒 Immutable — cannot be changedA string cannot be edited in place. Position 0 exists — reading name[0] would have given 'r' quite happily — but writing to it is refused. To get 'Ramesh' you must build a brand new string; the old one is never touched.
Three values, one instruction, two different answers — discovered rather than memorised. Compare the tuple program with the list program closely: the only difference in the whole file is ( against [. The tuple refused. The list did not even blink.
'str' object does not support item assignment sounds frightening and says something very simple: a string will not let you assign a new item into it. It is Python telling you, politely, that strings are immutable.3The two lists
Here is the classification in full. The good news is that you do not have to memorise both columns — there are only three mutable types in the whole language. Learn those three, and everything else is immutable by default.
int | 42 |
float | 3.75 |
bool | True |
complex | 3 + 7j |
str | 'hello' |
tuple | (10, 20, 30) |
NoneType | None |
list | [10, 20, 30] |
dict | {'name': 'Ramesh', 'marks': 87} |
set | {10, 5, -2} |
4“But I have seen a string change!”
This is the point where everybody objects, so let us settle it. There are two programs that look exactly like a string changing. Neither of them changes a string.
1. A method hands back a new string
word = 'hello'
print(word.upper()) # a new string comes back
print(word) # the original never changedHELLO hello
Did 'hello' change into 'HELLO'? It did not. upper() built a brand new string and handed it back. The original 'hello' is still 'hello', exactly as it was, untouched somewhere in memory.
2. The same name is given a new value
This one is far more convincing, because one name really does print two different things:
s = "hello"
print(s)
s = "bye"
print(s)hello bye
So the string changed? No. Nothing was written into the hello box at all. The line s = "bye" is two separate events, and neither of them edits a string:
"bye" is a brand new string, so Python reserves a fresh piece of memory for it and puts bye in there. It is not written on top of hello.
A name can point at only one box at a time. So the label s is peeled off the hello box and stuck on the new one. s now leads somewhere else.
This is the memory wall from the assignment lesson, and it is the honest picture. Run it line by line and keep your eye on where the label s is:
= runs.The word hello is still sitting in memory at the end, letter for letter. Nothing rubbed it out and nothing edited it — it has lost its label, that is all, so no line of your program can reach it any more. (Python tidies away values nothing can reach. Tidying away is not editing.)
And none of this is about strings. Swap in a number and the picture does not change at all — n = 5 then n = 7 gives 7 a box of its own and moves the label n onto it, because the number five cannot become seven. Try the integer and float tabs above and watch the same thing happen. Reassignment works this way for every immutable type.
s[0] = 'H' asks Python to edit the value that s points at — that is mutation, and a string refuses it. s = "bye" does not touch that value at all; it points the name at a different value — that is reassignment, and it is allowed for every type in Python. An immutable value can never be mutated. Its name can always be reassigned.One more thing follows from all of this, and it is the whole of the next lesson. If a name is only a label, then two labels can sit on one value. Whether that is handy or dangerous depends on exactly what you have just learnt — whether the value can be changed.
5Why does any of this matter?
Some data should not be editable — a date of birth, a set of exam marks already submitted. Put it in a tuple and Python guards it for you.
Some data must be edited all the time — a list of students in a class. Put that in a list, and change it as often as you like.
"Which of these is immutable?" is one of the most common one-mark questions in the paper. It is free marks, if you know the short column.
Try the refusals yourself. Python is very clear about them:
6Recap
list, dict, set. Everything else — int, float, bool, complex, str, tuple, None — is immutable. A method like upper() does not change a string; it returns a new one. And giving a name a new value — s = "bye" — does not change the old value either: the new value gets its own box and the name is moved onto it.Which of these is mutable?
After name = 'ramesh', why does name[0] = 'R' give a TypeError?
After word = 'hello' and print(word.upper()), what is inside word?
A program runs s = "hello" and then s = "bye". What happened to the string hello?
Why is n = 5 followed by n = 7 not an example of a value being changed?