Everything in Python is an object, and almost everything has attributes and methods, but different programming languages define “object” in different ways. In some, it means that all objects must have attributes and methods; in others, it means that all objects are subclassable. In Python, the definition is looser: everything is an object in the sense that it can be assigned to a variable or passed as an argument to a function. The official documentation of the language says that objects are Python’s abstraction for data.
All data in a Python program is represented by objects or by relations between objects.
Every object has an identity, a type and a value. An object’s identity never changes once it has been created (a good mental model is to think of it as the object’s address in memory). The
is operator compares the identity of two objects and the
id() function returns an integer representing its identity. For the CPython, the standard implementation of language enterpreter,
id(x) returns the memory address where
x is stored.
An object’s type defines the possible values for objects of the certain type and determines the operations that the object supports (e.g., print, length, etc). The
type() function returns an object’s type (which is an object itself). Like its identity, an object’s type is also unchangeable.
The value of some objects can change. Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created are called immutable. Lets explore this two properties further.
Objects whose value can be mutated. In Python they are objects of these types:
Here we should also mention that all the types are containers: objects that contain references to other objects. The references are part of a container’s value. In most cases, when we talk about the value of a container, we imply the values, not the identities of the contained objects; however, when we talk about the mutability of a container, only the identities of the immediately contained objects are implied. The important takeout here is that an immutable container (like a tuple) contains a reference to a mutable object, its value changes if that mutable object is changed.
Objects whose value can not be mutated:
To explain the immutable types further, we need to talk about the assignment statements in Python. Let’s say we have two variables,
b, with the identical values (for this and other experiments we will use
The question is: are they two separate entities holding the similar information, or they are two links, holding the reference to the same object?
To answer this question, lets check the id of both:
As we can see, the two different variables have the same id, which means they are just a references to the one string object, in our case represented by the
“String” value, like it is shown below:
Lets test test whether two names have the same value using the
It is obvious that in our case they carry identical values. But what about the reference and how to test weather they refer to the same object? In Python we can do it using the
This tells us that both a and b refer to the same object, and from here we can understand the fact that since strings are immutable, Python optimizes resources by making two names that refer to the same string value refer to the same object.
This is not the case with lists:
Since lists are mutable,
b have the same value but do not refer to the same object. The state diagram for this case will look like this:
This allows to keep track of mutations of each list. We can change values of the particular list and append new values to it without worrying that our actions will affect the other one. But is it possible for two variables to refer to the same object? To answer to this question, lets play with our list again. We have
a = [1, 2, 3] like in previous example. Now let’s assign
b and then check weather they refer to the same object:
Since variables refer to objects, if we assign one variable to another, both variables refer to the same object. Because the same list has two different names,
b, we say that it is aliased. We have to be careful with cases like this, because changes made with one alias affect the other:
Here we have mutated the object by interacting with b variable, and since both variables refer to the same object, a was also affected. The state diagram for our current case looks like this:
Although this behavior can be useful, it is sometimes unexpected or undesirable. In general, it is safer to avoid aliasing when you are working with mutable objects. Of course, for immutable objects, there’s no problem. That’s why Python is free to alias strings when it sees an opportunity to economize.