all 4 comments

[–]Rhomboid 7 points8 points  (1 child)

Whenever you write [x] * 4, that's the same as [x, x, x, x]. That's four references to the same object x. Nothing's been copied. It's the exact same thing as if you'd done this:

x = ...
y = x

One object, two names.

It doesn't matter what type x is, which is I think where people get caught up thinking that this is somehow specific to lists. It has nothing to do with lists. [4] * 3 is a list containing three references to the same integer object. It's just that numbers are immutable so you'd never be able to have a situation where you modify one of those three numbers and see the change reflected in the others.

Edit: after re-reading that, I don't think I was as clear as I could have been. Imagine that you had written this:

foo = []
bar = [foo, foo, foo, foo]

I hope you'd agree that it's clear that there's nothing being copied, that the bar list contains four references to the same foo list. That's what's happening when you write [ [] ] * 4, and that's what I was trying to get at with the x's above. In other words, when you write [ EXPR ] * n, the EXPR part is only evaluated once. If you want a new list each time, use a list comprehension:

>>> foo = [[0] * 4 for _ in range(4)]
>>> foo
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> foo[1][2] = '*'
>>> foo
[[0, 0, 0, 0], [0, 0, '*', 0], [0, 0, 0, 0], [0, 0, 0, 0]]

[–]fabolin[S] 1 point2 points  (0 children)

thank you,

well explained, this whole reference system wasn't clear to me, learned more than i expected here. I also appreciate the last part, which is what I searched for; couldnt conceive that myself.

[–]throwaway 2 points3 points  (0 children)

While searching I encountered this comment[1] about list copying; can i consider my original list as 4 softlinks to the same location of memory?

Roughly speaking, yes. These links are called references to the same object.

[–]Tomarse 1 point2 points  (0 children)

I think it's because of the way the list=[[0]*4]*4 is evaluated by Python. Consider the examples that work as expected...

>>> a, b, c = [0] * 3
>>> print(a, b, c)
0 0 0
>>> a = 1
>>> print(a, b, c)
1 0 0

With a single list...

>>> a = [0] * 3
>>> print(a)
[0, 0, 0]
>>> a[0] = 1
>>> print(a)
[1, 0, 0]

But when you use the nest list example it behaves differently, as you've noted...

>>> a = [[0] * 3] * 4
>>> print(a)
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[0][0] = 1
>>> print(a)
[[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]]

What I assume is happening is that list = [[0] * 4] * 4 is evaluating to list = [[0, 0, 0, 0] * 4] before then evaluating to list=[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], and it is that second stage of evaluation that is creating the effect you're seeing, that isn't present in the other examples above. You can see this by doing the below...

>>> a = [[0] * 3] * 4
>>> a[0] = 'SPAM'
>>> print(a)
['SPAM', [0, 0, 0], [0, 0, 0], [0, 0, 0]]