Python3: Mutable, Immutable… everything is object!

Today, I will be exploring why everything in Python is an object. I will explain what this means exactly and describe how objects are used to represent values in Python. I will begin with a basic description of objects.

OBJECTS

In Python, an object represents a value that a variable can refer to. To put it simply, an object is a value that can be referenced. For example, if the variable n holds the value 7, the variable n references an object who’s value is 7. Below is the code representing the previous example.

#n is a variable
#object with value 7 created and referenced by n
>>> n = 7

All objects can hold a specific type of information, which is specified by the programmer. Objects are categorized by type. Some examples of different types are, integer, float, string, char, list, and tuple. These are just a handful. Integers represent numbers and floats represent floating point numbers. Char is short for character and string represents a sequence of more than one character. Strings are indicated by double or single quotes (“Hello”) and the char type is indicated by single quotes (‘t’). Lists and tuples, on the other hand, can hold many different data types. Lists are indicated by brackets ([7, 7.77, “Hello”, ‘t’]), while the value inside are separated by commas. Tuples are represented by parentheses, while the values inside are separated by commas. Below is an example showing each of these types.

>>> int_var = 7
>>> float_var = 7.77
>>> string_var = "Hello"
>>> char_var = 't'
>>> list_var = [7, 7.77, "Hello", 't']
>>> tuple_var = (7, 7.77, "Hello", 't')

Just to make sure everything is clear so far, in the example above, string_var is a variable and “Hello” is a string object. Python has a built-in method called type(), which can be used to retrieve the type of an object. The type() method can be passed an object or it can be passed a variable that is referencing an object. The type() method, and other method similar to it, can be used to create important usage guidelines for a program or to manipulate specific types of data into other types. Below is a code example of what information the type() method retrieves.

>>> int_var = 7
>>> float_var = 7.77
>>> string_var = "Hello"
>>> char_var = 't'
>>> list_var = [7, 7.77, "Hello", 't']
>>> tuple_var = (7, 7.77, "Hello", 't')
>>> type(int_var)
<class 'int'>
>>> type(char_var)
<class 'chr'>
>>> type(string_var)
<class 'str'>
>>> type("Hello")
<class 'str'>

At this point, we know that objects are simply values and these objects can exist as different types of values. These types allow programs to define different types of information, which is essential for creating data structures. The built-in method type() can be used to retrieve the type information of a specific object. Other built-in methods can be used to retrieve other categories of information. The id() built-in method can be used to retrieve an objects location in memory.

OBJECT IDENTITY

If an object exists, it has a location in memory. This location is represented by an address. You can use the built-in method id() to retrieve the memory address of an object. Below is an example of id() showing an objects address in memory.

>>> n = 7
>>> id(n)
10116199

The id() built-in method returns the identity of an object, an integer that represents the object’s memory address. This address is unique to the object for the duration of it’s lifetime. The number (10116199) is the address of the object represented by 7. To be more clear, the address does not represent the location of n, but the location of the object that n is referencing. I will attempt to paint a better picture below. This example will hopefully demonstrate the distinction between variables and objects.

>>> x = 7
>>> n = 7
>>> id(n)
10116199
>>> id(x)
10116199

Here we can see that the address retrieved by id() is actually representing an object who’s value is 7. From this perspective, we could call this object “Object 10116199”, but we’ll just call it “Obj 199”. In the above code, the variable x and n are created, but Obj 99 is the only object that is created and it’s value is 7. We can use the “is” operator to test whether different variables share the same object. Below is a code example of using the “is” operator.

>>> n = 7
>>> x = 7
>>> n is x
True
>>> id(n)
10116199
>>> id(x)
10116199

Here we can see that the “is” operator correctly determines that n and x refer to the same object, and returns True. However, if x were to reference a different value, the two variable would be referencing two separate objects. Let’s look at another example.

>>> n = 7
>>> x = 9
>>> n is x
False
>>> id(n)
10116199
>>> id(x)
10782037

In the above example, two objects are created. The variable n references one object, who’s value is 7, and the variable x references another object, who’s value is 9. The “is” operator’s return value indicates that, n and x, are in fact, separate objects. In more Pythonic terms, n is not x. The id() built-in method confirms that fact. When x and n hold the same value, why is one object shared between them? The purpose is for python to be more efficient. Created objects exist in memory and take up space, sharing objects among variables allows for less memory allocation. Integer objects are not the only object types that have this characteristic. Below is an example of two strings sharing the same object.

>>> string_1 = "Ant"
>>> string_2 = "Ant"
>>> id(string_1)
67492099
>>> id(string_2)
67492099

Here we can see that string_1 and string_2 reference a shared object. Notice in the next example, string_1 and string_2 will have two separate objects.

>>> string_1 = "Ant"
>>> string_2 = "ant"
>>> id(string_1)
67492099
>>> id(string_2)
67492072

If you pay close attention to this example, you will notice that 2 things have changed from the previous example. The first is that the ‘a’ in the string_2 object is lowercase while the ‘A’ in the string_1 object is uppercase. The second is that both variables are referencing two different addresses. String_1 is referencing address 67492099 and string_2 is referencing address 67492072. This difference in addresses confirms that these variables reference two separate objects. Why is this? Object sharing is only possible when the desired value is consistent. In this case, “Ant” and “ant” are two completely different values.

Read the code below and try to follow how the values are being passed. Try to keep track of the number of objects created.

>>> x = 7
>>> n = x
>>> id(x)
76087595985
>>> id(n)
76087595985
>>> x = 22
>>> print(x)
22
>>> print(n)
7

What is happening here with the values may seem puzzling at first, but it really is quite simple. First, an object holding the value 7 is created, we’ll call it obj_7. The variable x is assigned to reference obj_7 on the same line. On the next line, the variable n is assigned to reference obj_7 as well. At this point, variable x and variable n both point to obj_7, this is confirmed by the id() method’s return values found on the following lines. The initial confusion happens in the following code.

>>> x = 22
>>> print(x)
22
>>> print(n)
7

Here, when variable x is assigned the value 22, a new object is created that is separate from obj_7. We’ll call this new object obj_22. So obj_22 is created new and x is assigned to it. Lets pause and think about where that leaves n. The variable n was originally assigned to the same object that x was assigned to. That object was obj_7. Does the expression “x = 22” change which object the variable n points to? Does that expression change the value of the object pointed to by n? The simple answer is no. The variable n still point to obj_7, who’s value is still 7, and the variable x points to a new object, who’s value is 22.

In essence, objects are just values. The objects how carry common values may be shared amongst other variables. Here we talked about objects in context of variables, but you’ll soon find out that almost everything in python is an object.