TIL: Python Comprehensions Make Matrices Work

Ry on December 04, 2018

Hello world! First, a super brief introduction: my name is Ry, and Iā€™m midway through a web development bootcamp in Chicago. I was a consultant ... [Read Full]
markdown guide
 

Hi Ry! I laughed reading this so it's a good start :-)

What you're experiencing is a combination of how Python syntax works and the fact that lists are mutable objects.

In the first example:

sleeve = [0] * 5
shirt = [sleeve] * 5

you're telling Python: "make sleeve a list of five numbers and then make shirt a list of five sleeves". So Python is going to copy the object pointed by the variable sleeve inside shirt five time, but it's the same object. You can easily see it like this:

>>> sleeve = [0] * 5
>>> shirt = [sleeve] * 5
>>> shirt
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> [print(id(l)) for l in shirt]
4384048520
4384048520
4384048520
4384048520
4384048520

id() is a function that returns the memory identity (address) of the object. As you can see, it's all the same object

The second example is basically the same, you're telling Python to multiply by five times the same list:

>>> shirt = [[0] * 5] * 5
>>> [print(id(l)) for l in shirt]
4387549256
4387549256
4387549256
4387549256
4387549256

In the third example you're actually building a new list for each iteration in the list comprehension, so it works:

>>> shirt = [[0 for i in range(5)] for j in range(5)]
>>> [print(id(l)) for l in shirt]
4387549896
4387550472
4387550344
4387549704
4387205896

:-)

 

! This makes significantly more sense -- thank you!

Why does the first [0]*5 not run into the same issues? Is it because this is updating a single list?

 

Why does the first [0]*5 not run into the same issues? Is it because this is updating a single list?

That's because Python cheats a little bit. I don't know the exact list of optimizations but you're creating a list of integers so I think it reuses the same object in memory. An integer is an immutable object, it detects you're creating a list of 5 integers, so it just fills up a list of 5 zeroes. You can see what's going on:

>>> s
[0, 0, 0, 0, 0]
>>> [id(x) for x in s]
[4501366592, 4501366592, 4501366592, 4501366592, 4501366592]

they all are the same object in memory but since you can't change it, it's fine.

Ooooohh, that makes sense -- thank you!!

 

The answer is really simple: how many lists do you make? If you really want a 5x5 zero matrix, it's quite obvious you need 6 lists. If you make only 2 of them, some (many) of them are going to be the same.

code of conduct - report abuse