Posts: 7
Threads: 2
Joined: Nov 2019
Between:
tt = [0,1]
a1 = [tt]*4
a2 = [tt for _ in range(4)]
a3 = [[0,1]]*4
a1[0][1] = 6
a2[0][1] = 6
a3[0][1] = 6
print(a1)
print(a2)
print(a3) Output: [[0, 6], [0, 6], [0, 6], [0, 6]]
[[0, 6], [0, 6], [0, 6], [0, 6]]
[[0, 6], [0, 6], [0, 6], [0, 6]]
&:
b1 = [[0,1] for _ in range(4)]
b1[0][1] = 6
print(b1) Output: [[0, 6], [0, 1], [0, 1], [0, 1]]
I can't understand this difference in result, can you help me?
Posts: 4,220
Threads: 97
Joined: Sep 2016
Lists are mutable. So if you reassign one, it doesn't reassign the whole list, it doesn't make a copy of one. You just now have two names pointing to the same list. And if you make a list of lists (if you're not careful), you end up with a list of references to the same list.
So the first two lists you have, a1 and a2, and just a bunch of references to the same list that tt is pointing at. If you change any one of them, that changes the list they are all pointing at. Now you can look at any of them, and they are all pointing at the original list which you changed.
a3 is similar, in that it's one list. The multiplication just makes more references to that same list. It's a different list than tt, but a3 is still four things pointing to the same list.
b1 is different because each time through the loop in the list comprehension, the list is reevaluated. So it is four pointers to four different lists. They are still pointers, though. So if you assigned cc = b[0], and changed cc, b[0] would also reflect those changes.
Posts: 7
Threads: 2
Joined: Nov 2019
Thanks a lot
Okay, so we can say that a1, a2, a3 and b1 have 1 common point: they all build a list of lists.
For a1, a2 & a3, the items that make up their list of lists all point to the original list.
For exemple:
tt = [0,1]
a1 = [tt]*4
a2 = [tt for _ in range(4)]
a1[0][1] = 6
#Or
a2[0][1] = 6
print(tt) Output: [0, 6]
The elements that make up the b1 list of lists all point to themselves in the original list.
For exemple:
b1 = [[0,1] for _ in range(4)]
b1[0][1] = 6
cc = b1[0]
print(cc)
print(b1) Output: [0, 6]
[[0, 6], [0, 1], [0, 1], [0, 1]]
cc[1] = 2
print(cc)
print(b1) Output: [0, 2]
[[0, 2], [0, 1], [0, 1], [0, 1]]
tt is assigned by a list. So for a1 & a2 we copy n times (by multilication and iteration) into a list some references of the list tt.
For a3 the sub-list will be assigned in literal form. In a list, some references from this literal list will be simply copied n times (by multiplication) for a result similar to a1 & a2.
For b1 the sub-list will be assigned in literal form and re-evaluated for each copy (by iteration) in a list for n independent copy.
So for a2 =[tt for _ in range(4)], tt is not re-evaluated because it refers to the list assigned to it, unlike b1 = [[0,1] for _ in range(4)].
For a1 & a3 it is a simple copy, from a list assigned for a1 and from a list expressed literally for a3.
Is it correct?
Posts: 1,950
Threads: 8
Joined: Jun 2018
It's easy to observe what happens:
>>> lst = [0, 1]
>>> first = [lst] * 4
>>> [id(item) for item in first]
[4443423296, 4443423296, 4443423296, 4443423296] # referencing same object
>>> second = [lst for _ in range(4)]
>>> [id(item) for item in second]
[4443423296, 4443423296, 4443423296, 4443423296] # referencing same object
>>> third = [[0, 1] for _ in range(4)]
>>> [id(item) for item in third]
[4443415872, 4442844672, 4443418112, 4443422912] # referencing different objects
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy
Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Posts: 7
Threads: 2
Joined: Nov 2019
Well, I do not want to observe, I want to understand the process by reading the code without having to do tests.
And I'm talking about list of lists.
Posts: 4,220
Threads: 97
Joined: Sep 2016
By observing you can understand. The id() function perfringo is using shows the address of the item in question. So he is showing that in the first two instances, it's all residing in the same memory address: it's all pointing to the same list. In his last example, there are four different addresses, indicating four different lists.
Posts: 7
Threads: 2
Joined: Nov 2019
Of course, and not only I can understand but especially I can learn.
Your help to both was relevant and clear, thank you for your concern.
But in my answer (#3) I try to explain the reading of the code, when I read or write it, from a semantic point of view.
To make the difference, for example, between [tt for _ in range(4)] and [[0,1] for _ in range(4)] which gives different results.
I try to explain it as follows: in both cases we create a list of lists by iteration [... for _ in range(4)].
In the case with tt, the copied list is a variable that contains a list, which is why tt is not re-evaluated at each iteration, because it refers to the list that has been assigned to it and will give n copy with the same reference.
In the other ([0,1]) it is the literal expression of a list that contains two elements, this is the reason why [0,1] is re-evaluated at each iteration and which will give n independent copy therefore with different references.
In reading or writing the code the difference to be identified between [tt for _ in range(4)] and [[0,1] for _ in range(4)] is tt & [0,1], variable against literal form in list of lists creation by itération.
And of course we cannot identify this differences if we have not understood it and observed it.
|