List comprehension value modification

Hello,
while I was working on a list comprehension, I met that:

inerList = [None for i in range(3)]
allList = [inerList for i in range(3)]
allList[0][0] = "X"

>>> [['X', None, None], ['X', None, None], ['X', None, None]]

and

allList2 = [[None, None, None], [None, None, None], [None, None, None]]
allList2[0][0] = "X"

>>> [['X', None, None], [None, None, None], [None, None, None]]

Why the list comprehension doesn’t get the only first index list when I try to update it?

That’s interesting behavior… I’d guess it has to do with the way list comprehension uses pointers behind the scenes (though a second opinion would be good)

If a list added is a pointer as opposed to a hard copy, than modifying the original would effect every element. For example in a similar way, if we have:

a = [1, 2, 3]
b = [a for i in range(3)]

Then we find modifying a also modifies b:

a[0] = 10
print(b)
>>> [[10, 2, 3], [10, 2, 3], [10, 2, 3]]

Likewise modifiying b changes a:

b[0][0] = 10
print(a)
>>> [10, 2, 3]

Again this being due to pointers is more of a guess on my part than fact, I haven’t dug through Python’s code so perhaps someone with more knowledge could step in to confirm or deny.

Though a simple fix for this though could be to use list() which returns a new list as opposed to a pointer:

a = [1, 2, 3]
b = [list(a) for i in range(3)]

b[0][0] = 10
print(b)

>>> [[10, 2, 3], [1, 2, 3], [1, 2, 3]]
2 Likes

Pretty much, they’re all the same list (the same object). It’s much like writing-

lst = [1, 2, 3,]
lst_of_lists = []
lst_of_lists.append(lst)
lst_of_lists.append(lst)
lst_of_lists.append(lst)

So a modification to the original lst or any reference to it e.g. modifying the value at lst_of_lists[2] would alter the same object.

2 Likes

Thank you, it makes sense. I would thought a object in an object stay “independent”.

edit:
Actually it is the same object until I make a list() of the first list as you suggested.

1 Like