An Object is an Object is an Object
Python is an object-oriented programming (OOP) language. This means every data type ultimately stems from an ‘object’ class. Another way to say this is everything is an object. Understanding how objects work in Python can be a little confusing so let’s take a closer look.
Python has two built-in functions, id() and type(). These functions give you information about a particular object. As you’ve probably and so intelligently deduced, id() returns the id, or memory location of the object:
and type() tells you what kind of object it is; more specifically what ‘object’ sub-class it is:
You might be thinking “uhh.. I already know ‘2’ is an integer. Why do I need a function to tell me that?” Well, what if the object you’re dealing with is not obvious:
Or more importantly, what if your code has a conditional statement that executes a different task depending on which object type you’re passing?
On the face of it, you’d think all objects are mutable, meaning they can be mutated, or changed. After all we can declare an integer with one value and then seemingly change it to a new number:
An integer, however, is and immutable object. This means while it seems you’re just changing the value of ‘i’, using id() will show you the reassignment, i = 10 places ‘i’ in a new location:
We see here i’s original id 10055680 becomes 10055840 after assigning 10 to it. So what’s really happening here? Let’s just say the ‘i’ you thought you knew isn’t the same ‘i’ at all! Since the int type is immutable, the ‘i’ you’re assigning 10 to isn’t the same ‘i’ that was assigned 5; they are two different i’s in two different memory locations as evidenced by the different id’s.
Let’s make this a bit easier. Traditionally we’re told that variables are boxes that we put our values in. Since this isn’t an accurate statment, it has the potential of leading people down a path of incorrect assumptions about objects. In the traditional value-in-a-box, the box (variable) is getting the spotlight as it houses the value. But why should the box get all the credit? Let’s flip it around. Instead of saying “the box has a value in it”, we’ll say “the value has a label on it.” So in the example above: instead of “we place the value 5 inside the variable ‘i’”, we’d say “the value 5 is assigned the label, ‘i’. Then when we need ‘i’ to have a different value, we put a 10 somewhere in memory and say “the value 10 is assigned the label, ‘i’.
On flip side, the object type ‘list’ is a mutable object. What does this mean? You guessed it. When appending a new value to a list, you actually ARE editing the the same my_list in the same memory location. So first we’d say “the value [1, 2, 3] has the label, my_list.”
This time, we see that the before and after id for my_list is the same 139696155492936. So instead of “the value [1, 2, 3, 4] is created in a new location and given the label, my_list,” we’d say “the value [1, 2, 3] with the label my_list becomes the value [1, 2, 3, 4].
So smarty pants, I know you’re now saying “who cares? why make some things mutable and others immutable. What’s the point?” Well, let’s say we create several immutable int objects with the same value of 5.
Since we’re in the habit of thinking values-in-boxes, we instinctively think a, b and c are distinct boxes, each holding their own 5. But what if we use our new way of thinking and say “the integer 5 has three labels?” We can immediately understand that there’s only one integer, 5.
See? a, b and c are all in the same location 10055680. This means they are all labels for the same value, 5. It’s like someone named Joseph having the nick-name’s Joe, Joey and JoJo. They’re different labels, but all refer to the same person. If, however, you change any of them to a different value, a new memory location is allocated for that value.
Here we see while ‘a’ and ‘c’ are still in the same location 10055680, ‘b’ has been placed in 10055744. All of this means if a variable is immutable, Python can make efficient use of resources by using up only one space for several variables.
Mutability has consequences for functions as well:
In this example, we have a function that claims it can change the immutable value of a string. I’d like to see you try buddy. Sure enough, our overly-confidant function falls flat on it’s face; str’s value is “Hello” both before and after the function call. But what about a function that takes a mutable value:
As expected, we’re able to pass the mutable list to a function and see it’s value changed after the function call ends. Namely, my_list is [1, 2, 3] before being passed to change_list() is [2000, 2, 3] after.
Here’s another example to illustrae Python’s efficiency:
Here we see ‘a’ and ‘b’ start with different values and therefore have different ids. If we add 2 to ‘b’ evaluating to 4, the location ‘b’ was referencing is freed up. ‘b’ is then set as a label for the same location that ‘a’ is referencing. We can see this efficient use of memory by looking at the resulting id’s. Both ‘a’ and ‘b’ are now in the same location.
Good. Now we understand that a variable name is a label placed on a value. This means that variables (labels) in Python are actually references to an object in memory (much like pointers in C).
Let’s illustrate the differences again between mutable and immutable objects.
Like int, the object type, string is also immutable. This means ‘a’ and ‘b’ are both labels for the same “Hello” in one memory location. They have the same id 140248293568272. Evaluating ‘a is b’ as True, Python gives us further confirmation of “Hello”’s singular location.
We then reassign ‘a’ and ‘b’ as references (labels) to a list instead of a string. Since lists are mutable, however, ‘a’ and ‘b’ aren’t references to the same object anymore. They are references for unique values each in their own memory locations. We can see this when using id() to check both their locations. ‘a’ sits at 140248286607112 while ‘b’ resides at 140248294374664.
It’s important to note certain immutable types contain indexed elements that may be mutable. A tuple for example is an immutable object that can hold values of different types. If one of those types is mutable (a list for instance), it can be changed even though it is inside the immutable tuple. First, see how trying to change the value of a tuple’s element returns an error:
This is because the value at a is an integer, which as we know is an immutable object type. If, however, we want to change or append an element of that list at a, we have no problem: