Python is an interesting programming language with a lot of features and utilities. It follows a work style architecture based on the principle of “First-Class Everything”. This concept centers around the idea of having every single item of data in python belonging to a class as an object with attributes and methods.
This means that things such as integers, floats, structures of data, functions, and strings are all objects. They can all be stored in variables, passed between functions, and even be returned from them. Variables are simply references and don’t actually hold data themselves, kind of like a pointer.
In the next image, references to objects “Hello Python” and [1, 2, 3] are created. In both cases, the variables don’t actually hold the data, but simply direct access to it.
id and type
Each object in python has an identity and a class it belongs to. The identity corresponds to an integer (or long integer) value which is unique to every object during its lifetime in a program. Two objects with different life times can have the same id value.
In order to obtain the identity of an object, the built in function id(object) is used.
In this example, an object with value “34” is created in memory and referenced through the identifier “a”. When id(a) is used and printed, a numeric value in shown.
If the program is run again, the value for the same object can change, as it is simply a representation of the object in memory (similar to a memory address).
In the case of type, the same value “34” we declared before is instantly assigned to the int class in python (integer). In order to check this, we can use the built in function type(object) .
We can create several types of variable, and see all the different classes they can belong to.
An object can also belong to a custom class created by a programmer, as seen in the next example.
Mutable objects and Immutable objects
Python has 2 different kind of objects in relation to mutability, mutable and immutable. These differentiate in the possibility of being modified or not while existing in memory.
If for example, we create a variable referencing an integer with value 2 (immutable), and then we try to add 34 to it, a new object will be created instead of changing the existing one.
We can verify this by printing the id before and after modification, which will not be same.
In other cases python will simply throw an error if there is an attempt to modify an immutable data type, such as shown next with a a tuple.
If instead we create a mutable object and then modify it, its id won’t change, as no new object will be created.
In order to save up memory space, python takes advantage of the immutability of some of its objects in order to reduce memory occupation in the system. An example of this is when we create a string. If the string is the same for more than 1 variable, then all references will be made to the same object, as shown below with the help of id().
This is the same case with other objects such as ints, floats, and booleans.
The exception for this rule is tuples and frozensets.
The reason for tuples being that although objects of this type are immutable, the objects inside it can be mutable.
The next example shows this behavior.
- A tuple of 2 positions is created with variable a.
- The objects inside the tuple are an integer and a list.
- The integer and the list will always keep the same id
- The list is a mutable object so it can be altered
- The modification of the list does not affect the immutability of the tuple itself
For the case of frozenset, there is not share-ability between variables due to its internal working measure which has to do with hash checking of values.
Why does it matter and how differently does Python treat mutable and immutable objects
It is important to know how python handles mutable and immutable objects in order to avoid getting errors or modifying data when that is not the desire.
An example of this can be shown with a list, a mutable object.
In this case, a list is created with the variable “a”. After that, “b” gets an assignment as if creating a copy, but when “b” is modified and then “a” is printed, the result is as if “a” had been modified instead of “b”.
The reason for this can be seen in the frames and objects space, where it is shown that both variables are actually pointing to the same object instead of different ones.
In order to do things with a copy, a different approach would have to be taken. A possibility would be to use a list method to create a copy.
It is also important to take into account that when mutable objects are modified, no new object is created, so the id is kept the same.
In the case of immutability, there must be precaution when data is modified in order to avoid errors.
If for example, a string was declared with a typo, it wouldn’t be possible to perform a direct modification to fix the error.
Instead, there would be a need to transform the immutable object into a mutable one, fix the error and then transform it back.
This process would create a new object with different id
How arguments are passed to functions and what does that imply for mutable and immutable objects
When an object is passed to a function, a new reference to it is created instead of a copy. This happens with both mutable and immutable objects.
In this example an immutable object of type int is passed to the function print_obj.
If the id of the object is printed outside and inside the function, it will be shown to be the same, meaning that in this case, both the variable “a” and the variable “obj”, have a reference to it.
When the function finishes its process, the variable “obj” is erased, but the object it was pointing to stays in memory because “a” is still pointing to it.
Just as before, if there is a modification attempt inside the function, then a new object will be created instead of altering the existing one. We can check this change with the difference in ids.
As with immutable types, when a mutable object is passed to the function, a copy isn’t created, but instead a new reference to the object. If there is a modification inside the function, this change will be notable outside too.
Garbage Collector in Python
As seen throughout the blog, python defines an object as an item present in memory, while variables are simply references to those memory spaces.
This references can be deleted by the user, where as objects themselves can only be removed by a Garbage Collector method inside python, which only acts when there are 0 references left to an object.
An example is shown next:
- An object type string is created with the value PYTHON.
- Two references to the same object are created “a” and “b”
- The id for both references are checked, which are the same
- The reference “a” is deleted and this can be checked by attempting to print its id, which results in an error.
- The object PYTHON still exists in memory, which can be checked by printing the id of “b” which still shows the same number as before and by seeing the Global frame space.
- After deleting the reference “b”, there are no more variables pointing to the object PYTHON, triggering the Garbage Collector and removing the object from memory, as shown in the frames, objects space.
- After this, a new object PYTHON is created with “c” variable referencing to it. The id is the same as previous references, but only because Python is recycling memory space.
- We have covered many concepts here, the most important thing is to practice