Written By Vishal Keshav


'Object'ive view of Python

Italian Trulli

Solve this first

Let’s start with analyzing what the output of the below python program.

l = [[]]*2
print(l)
for i in range(2):
    for j in range(3):
        l[i].append(0)
print(l)

If you said the output is

[[],[]]
[[0,0,0],[0,0,0]]

then read on. In this post, I am going pictorially outline how python treats its variables, which will eventually explain the correct output we would get from the above program.

Objects objects, everywhere!

In python, everything is an object (having some attributes and functionality). Even an integer is an object (where the value attribute is the value we assign to an integer). When we declare a variable var1 = 47, we are creating an integer type object somewhere in the memory having a value of 47, and var1 is just pointing to that object. Now, consider another object pointer var2 = [47]. This will create an object of type list somewhere in the memory and var2 will point to that object.

Pictorially, this will look something like this

Now let’s see what happens when we modify those two variables.

var1 = var1+1
var2[0] = var2[0]+1

This modification resulted to var1 = 48 and var2 = [48]. All these are consistent with what we think should happen, but under the hood, this is what happened.

The object that var1 was pointing to, got destroyed and a new object was created, whereas var2 object remained as it is, only the object at its index 0 got replaced by another int object. So, what going on?

To change or not to change, that is the question

It turns out that in python, not all objects have the same characteristics. Some can change (list, sets, dictionary are mutable) and some cannot (int, float, tuple are immutable). The immutability does not allow changing the value attribute of the int object (through re-assignment). Instead, upon re-assignment, the original int object reference count gets decremented and the garbage collector ends up destroying the object. The list object, however, mutates and lets the change happen on its 0th index.

Back to the point

When we declared var = [[]]*2, it ends up creating a mutable list object, and then creates another mutable object (again, of type list), placing it twice at index 0 and index 1 of the first list.

We can verify this programatically by printing the identity (sort of memory location) for all the objects.

print(id(l))
print(id(l[0]))
print(id(l[1]))

On my computer, I got these outputs

140110921293312
140110922225216
140110922225216

This tells us that we end up creating only two objects of type list.

    for j in range(3):
        l[i].append(0)

In the above code snippet, the object at location 128 has been referenced twice such that 0 is appended thrice each time. As a result, we get the following output:

[[0,0,0,0,0,0], [0,0,0,0,0,0]]

So, finally, how would we get the expected output [[0,0,0],[0,0,0]]? We need to declare the list object thrice. Below code snippet achives that.

l = [[],[]]
print(l)
for i in range(2):
    for j in range(3):
        l[i].append(0)
print(l)

Conclusion

We saw how python treats its variables and what do the variables mean to python language (they are just pointer to objects). This basic understanding sets us to understand the pass-by-value and pass-by-reference paradigm and how python is different in that respect. In the future article, I will explore more on this topic.