Let’s get into that.
One of the challenging concepts in Python is how variables are created and assigned.
In programming, sometimes the analogy of boxes is used.
Each box is a variable and the contents of that box is its value.
This is not a great analogy, and as I’ll show later on, can be very problematic, especially when considering Python.
Instead, it might be better to imagine variable names as sticker labels.
They can be placed on a box, but they don’t contain anything.
The box (an object) holds the value.
The sticker is just a label.
Moreover, more than one sticker can be placed on any box.
In short, we make changes to objects in Python, not their names.
Two Useful Functions: id() and type()In the examples that follow, I will be exploring how Python interacts with various mutable and immutable objects.
In order to fully examine its behavior, I will be using the id() and type() functions extensively.
First, let’s look at these two functions so that we an understand their outputs.
id() takes an object as an argument, and returns its id, a number.
At the low level, this is actually the memory address where, under the hood (in the CPython implementation), C is storing the object.
We can compare the ids of different names to see if they point to the same object or not, because each object has a unique id.
We’ll get into that later, but for now, a simple example of id() in use:>>> a = 5>>> id(a)10105216>>> b = 10>>> id(b)10105376Now, to look at type().
Like id(), type() takes an object as an argument, but returns the object’s class type instead of its id.
This is going to come in handy as we start to pick apart different types of objects, and their mutability/immutability.
Again, let’s see a simple example:>>> msg = 'hello'>>> type(msg)<class 'str'>>>> age = 10>>> type(msg)<class 'int'>Now that we have a basic understanding of type() and id(), let’s use them to start exploring objects in Python, both mutable and immutable.
Mutable ObjectsMutable objects are objects that can be changed in Python.
They are much less common than immutable objects and include: lists, sets, and dictionaries.
They have some interesting behaviors, that can seem a little confusing at first.
Let’s see some examples of what I mean:>>> list1 = [1, 2, 3]>>> list2 = list1>>> id(list1)140336099032264>>> id(list2)140336099032264>>> list2.
append(4)>>> list1[1, 2, 3, 4]Here, we’ve made a list called list1, and then assigned another name(list2) to that list.
Both names are pointing to the same object, as seen by their shared id.
We append the list by calling the name list2, but then print the list using list1.
It prints out our new list.
Why?.When we made list2, we didn’t make another object, we simply made another name that points to the same object as list1.
And methods don’t act on names, they act on objects.
So when we type: “list2.
append(4)”, we’re really saying: “append 4 to the list object that list2 points to”.
What if we wanted to make a change to list2 but not list1?.Well, we’d have to first copy that list.
>>> list1 = [1, 2, 3]>>> list3 = list1[:]>>> id(list1)140336099032264>>> id(list3)140336098233352>>> list3.
append(4)>>> list1[1, 2, 3]We copied the object that list1 points to, and then made a new object from that copy.
Now, when we change something in list3, the change isn’t reflected in list1, because we aren’t altering the same object.
The same thing can be demonstrated by creating two lists which contain the same elements.
>>> list1 = [1, 2, 3]>>> list2 = [1, 2, 3]>>> id(list1)140397858622984>>> id(list2)140397851306184Each of the elements(integers, in this case) are immutable, but the lists themselves are mutable.
We can append, pop, and modify them however we want, and we won’t make any changes to the other list.
This isn’t true for immutable objects.
Immutable ObjectsImmutable objects form a majority of the objects that we’ll be interacting with in Python.
They include things like strings, ints, floats, and tuples.
Let’s look at that last example, but this time we’ll use two strings, instead of two lists:>>> string1 = "hello">>> string2 = "hello">>>id(string1)140336098225712>>>id(string2)140336098225712Look at that!.Both string1 and string2 have the same id, meaning they’re actually the same object.
How could this be?.Well, it comes down to the fact that strings are immutable.
The same holds true for other immutable objects as well:>>> a = 5>>> b = 5>>> id(b)10105216>>> id(a)10105216Now, if we change the value of a, its id should change, right?>>> a = 4>>> id(a)10105184Exactly!.What we’ve done is place our name, a, on a new int object.
When we reassign a name that points to an immutable object, that’s exactly what we’re doing.
Mutable and Immutable Objects in FunctionsThe properties of mutable and immutable objects make it so that they behave quite differently with respect to functions.
While changes to mutable objects exist outside of the scope of a function, changes to immutable objects do not.
Let’s look at a couple examples:>>> def strFunc(oldString).
oldString = "goodbye".
>>> oldString = "hello">>> strFunc(oldString)>>> print(oldString)helloWe updated the string inside the function, but when we exit out, it keeps its old value.
Note that there is no return statement.
If we had been returning the string and printing that value, it would be a different story.
Now, let’s try to do the same thing with a mutable object:>>> def listFunc(oldList).
oldList = 'goodbye'.
>>> oldList = ['hello']>>> listFunc(oldList)>>> print(ListFunc)goodbyeIn this case, we sent a mutable object into a function.
We then updated a member of the list, and now that update exists outside of the scope of the function.
Why Does This Matter?As we saw in the last section, knowing whether you’re working with a mutable or immutable object can have a non-trivial, practical effect on your code.
Python treats mutable and immutable objects differently, and that affects you as a programmer.
Understanding what kind of objects you’re working with can be super handy in avoiding errors and debugging code.
Looking into how Python really works is not just an academic exercise or a theoretical gander.
The wires behind Python hold the key to a better understanding of its quirks.
I, for one, am excited about exploring them, and continuing down the path of becoming a Python programmer!.