Python3: Mutable, Immutable… everything is an object!
In this article, I’m going to talk about the inner Python how and why things work as they do in this amazing language, I’ll be discussing what id and types are, what are mutable and immutable objects and why does it matter. How differently does Python treats mutable and immutable objects, and how arguments are passed to functions, and what does that imply for mutable and immutable objects.
Id and Type.
An important note is that everything on Python is an object, including numbers and instances of custom classes.
Each of these objects in python has a lifetime, and there's a thing called id, which is its location in memory, it will inevitably change the number once you run the program once again.
Mutable Objects.
These types of objects can be changed without changing their id (or creating a new object in other words) the mutable types are lists, sets, and dicts (dictionaries)
As we saw in the previous example we created a list called ‘a’ with the elements [1, 2, 3] then we look at its id with the function id() which takes an object as a parameter, it gives us the id that ends with 1840 (it will be different if you try it on your own) and then we changed the first element on the list (which is 1) for a 4, We looked at its id again and it’s still the same, that means that its the same object.
Maybe all of that didn’t make much sense, or you think it is not as important, but when we see immutable Objects it’ll be more clear why this is an important part of Python.
Immutable Objects.
These are the other types of objects in Python that cannot be changed, hence immutable. The immutable (and mutable) types are:
But how do they work?
Well, let’s try changing some immutable types on the Python interactive mode!
Let’s use a tuple called person, every person has a name, a year of birth, and a set of skills.
Here, our person is called Bob, he was born in 1991 and has a list of skills, that are sleeping and eating.
Let’s try to change Bob’s name to something else like Michael:
We immediately get
TypeError: ‘tuple’ object does not support item assignment.
This shows us that a tuple is indeed an immutable type.
Now let’s try changing Bob’s third letter ‘b’ to a ‘t’ so we get Bot, that doesn’t affect the tuple, right? that would just change one letter of the string “Bob”, let’s try it:
Seems like it didn’t work and gave us a similar error:
TypeError: ‘str’ object does not support item assignment.
You’ve probably guessed it by now, strings are also immutable types so we cannot change them.
Let’s do another example:
We declared a variable called person and assigned an str object “Bruce” and concatenated “ Wayne” to it and then printed person, but how were we able to do this if strings are an immutable type in Python!?
The answer is simple, the variable that only held “Bruce” at the beginning is different from the one that holds both “Bruce” and “ Wayne”, which means that by concatenating the string “ Wayne” to person we created and assigned a new string to person!
Let’s prove it in the terminal:
We did the same that was done in the previous example, but now we’re printing the id of person each time we do something with it.
first, when we assigned “Bruce” to person its id ended with 7488, but then we concatenated “ Wayne” to it, and its id changed to 8256 which means that we’re holding a different variable than the one we started with.
Functions and Objects
Everything we have seen so far give us some understanding of how Python handles its variables (often called names) and objects, but now let’s see how these objects work within functions. By default, Python passes function arguments by reference (that means that if the object gets modified in the function it also modifies the original object the variable is ‘pointing’ to), but there’s a catch if the object is immutable, Python doesn’t pass it as a reference but as a value (means that if the object gets modified in the function its changes won't be reflected on the original object).
To understand better let's do some examples:
Now let’s see what happened here. First, we declared a function ‘concatenator’ which takes a list as an argument (or parameter) and appends it the number 10, we then created a list called my_list that holds the values [1, 4] and printed its contents, then we called the function concatenator and passed our list as an argument, then we printed my_list contents once again and it printed [1, 4, 10] which means that we modified the original object hence pass by reference because its a mutable object.
Now let’s do a similar example for an immutable object like an integer:
Let’s explain this example once again, there’s a function increment that sums 10 to the number passed as an argument, we created an integer lucky_number that holds the number 3, then we called the function increment and passed lucky_number as an argument and printed its value, but surprise, the value it’s still the same, which means that the original wasn’t modified, pass by value.
The one modified was the one that the function had, the value stayed there, that's the function scope, let’s see that in action:
That’s the same example as before but something changed, we added a print inside the function increment, that when called prints 13, that's because the function actually got a 3 and operated with it but as it was passed by value the variable scope made it stay there.