Every Python user has come across the terms ‘mutable’/’immutable’ at one point or the other. It’s a property that determines the changeability of the object. If something in python is mutable, its value can be changed and if it’s not mutable, it’s value is fixed. For example, once a tuple is defined, a new member cannot be appended to it nor an existing member can be removed from it. Trying to do so invokes error.

While Lists are different. They allow you to play around with their values.

Fig. (ii)


So, lists are mutable but tuples aren’t. Simple, isn’t it?

Well, not quite so.

First we need to understand that everything in Python is represented as Objects. Numbers (integers, real, complex), Strings, Tuples, Lists, None, Bytes etc are all objects. These objects have three main attributes, an identity, a type and a value. The identity is similar to the C++ memory address. It never changes once an object has been created. This identity can be checked with id() function.

Fig (iii)


The other two attributes determine the operations and value that the object can have. The type() functions returns another object that determines the operations supported like length(), append() etc. See error details of Fig (i) for details. There it says, there is no ‘append’ method for tuple. This attribute type is also unchangeable. The third attribute value is changeable for some objects. Objects whose value attribute can be changed are mutable and other are immutable.


Example,
someInt = 3
Here someInt is a object of type Integer (Int). It has an identity given by:
id(someInt)    #139803249609536
type(someInt)   #int

This type() value, ie ‘int’ here, determines the other properties that someInt can have and operations that it can support. This differentiates one data type from other. The ‘3’ we passed (or assigned) to someInt is its value.

Mutability

The identity of an object never changes. And the changeability of its value determines the mutability of that object.

Strings (let’s say) are immutable data types. That means the value of a string cannot be changed once it is created. But lets look at the following snippet.

Fig (iv)


We are able to change the value of someString from 'foo' to 'foobar'. This violates the definition of immutability mentioned above. This is where the confusion lies. Actually something different is happening behind the scenes. We are not actually changing the value of the someString object. In fact, we are creating a new object with the updated value. This can be verified by the identity of someString before and after updating the value.
Remember, identity of the object never changes once it is created.

Fig (v)


We can clearly see that after update, a new object was created with the new value and was referenced by someString. So when we think that we are changing the value of immutable data type, we are not actually doing so.
Let’s look what happens when we change the value of mutable objects.

Fig (vi)


We can see by the value returned by the id() function that we are changing the value of same object.

Scenario:
If tuples are immutable and list are mutable, can I change the value of list inside a tuple?

Yes. Let’s have a look.

Fig (vii)


But what’s going on?

The answer is quite simple actually. someTuple contains the reference to the mutable data type list ( [1,2,3] in this case ). Its just a reference and not the actual data. And by reference, I mean the identity of the data, a list, a integer and a string in this case. So when the value of the list changes, the reference in the tuple stays the same since list is mutable and changing the values of list does not create a new object. However, the tuple is still immutable as the collection of object references it holds are still the same.

Conclusion:
Having an unchangeable content is not same as immutability as demonstrated by the scenario above where immutable tuple has changed contents. Immutability is determined by the type of the data. Numbers, Strings and Tuples are immutable while lists and dictionaries are mutable.

One More Thing:
If new objects are created when we update the value of immutable data types, what happens to the old ones?

The answer again is simple. The old ones becomes unreachable and Python, being awesome as it is, automatically garbage-collects them. Or so I think.