목록 변경 사항 목록이 예기치 않게 하위 목록에 반영됨
Python으로 목록 목록을 만들어야했기 때문에 다음을 입력했습니다.
myList = [[1] * 4] * 3
목록은 다음과 같습니다.
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
그런 다음 가장 안쪽 값 중 하나를 변경했습니다.
myList[0][0] = 5
이제 내 목록은 다음과 같습니다.
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
내가 원하거나 기대했던 것이 아닙니다. 누군가가 무슨 일이 일어나고 있는지, 어떻게 극복 해야하는지 설명해 주시겠습니까?
답변
글을 쓸 때 [x]*3
본질적으로 목록을 얻습니다 [x, x, x]
. 즉, 동일한 x
. 이 싱글을 수정하면 x
세 가지 참조를 통해 볼 수 있습니다.
x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(l[0]): {id(l[0])}\n"
f"id(l[1]): {id(l[1])}\n"
f"id(l[2]): {id(l[2])}"
)
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048
x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
이를 수정하려면 각 위치에 새 목록을 만들어야합니다. 한 가지 방법은
[[1]*4 for _ in range(3)]
[1]*4
한 번 평가하고 1 개의 목록을 3 번 참조하는 대신 매번 재평가 합니다.
*
목록 이해가하는 방식으로 독립된 개체를 만들 수없는 이유가 궁금 할 것입니다. 곱셈 연산자 *
가 표현식을 보지 않고 객체에서 작동 하기 때문 입니다. 를 사용 *
하여 [[1] * 4]
3 을 곱 하면 식 텍스트가 아닌 *
요소 1 개 목록 만 [[1] * 4]
평가됩니다 [[1] * 4
. *
해당 요소의 복사본을 만드는 방법도, 재평가하는 방법도 모르고 [[1] * 4]
, 복사본을 원하는지도 모르고, 일반적으로 요소를 복사하는 방법조차 없을 수도 있습니다.
유일한 옵션 *
은 새 하위 목록을 만드는 대신 기존 하위 목록에 대한 새 참조를 만드는 것입니다. 다른 것은 일관성이 없거나 근본적인 언어 디자인 결정을 대대적으로 재 설계해야합니다.
대조적으로, 목록 이해는 모든 반복에서 요소 표현식을 재평가합니다. [[1] * 4 for n in range(3)]
재평가 [1] * 4
같은 이유로마다 [x**2 for x in range(3)]
재평가 x**2
마다. 의 모든 평가는 [1] * 4
새로운 목록 을 생성하므로 목록 이해가 원하는대로 수행됩니다.
Incidentally, [1] * 4
also doesn't copy the elements of [1]
, but that doesn't matter, since integers are immutable. You can't do something like 1.value = 2
and turn a 1 into a 2.
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]
Live Python Tutor Visualize
Actually, this is exactly what you would expect. Let's decompose what is happening here:
You write
lst = [[1] * 4] * 3
This is equivalent to:
lst1 = [1]*4
lst = [lst1]*3
This means lst
is a list with 3 elements all pointing to lst1
. This means the two following lines are equivalent:
lst[0][0] = 5
lst1[0] = 5
As lst[0]
is nothing but lst1
.
To obtain the desired behavior, you can use list comprehension:
lst = [ [1]*4 for n in range(3) ] #python 3
lst = [ [1]*4 for n in xrange(3) ] #python 2
In this case, the expression is re-evaluated for each n, leading to a different list.
[[1] * 4] * 3
or even:
[[1, 1, 1, 1]] * 3
Creates a list that references the internal [1,1,1,1]
3 times - not three copies of the inner list, so any time you modify the list (in any position), you'll see the change three times.
It's the same as this example:
>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
where it's probably a little less surprising.
Alongside the accepted answer that explained the problem correctly, within your list comprehension, if You are using python-2.x use xrange()
that returns a generator which is more efficient (range()
in python 3 does the same job) _
instead of the throwaway variable n
:
[[1]*4 for _ in xrange(3)] # and in python3 [[1]*4 for _ in range(3)]
Also, as a much more Pythonic way you can use itertools.repeat() to create an iterator object of repeated elements :
>>> a=list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0]=5
>>> a
[5, 1, 1, 1]
P.S. Using numpy, if you only want to create an array of ones or zeroes you can use np.ones
and np.zeros
and/or for other number use np.repeat()
:
In [1]: import numpy as np
In [2]:
In [2]: np.ones(4)
Out[2]: array([ 1., 1., 1., 1.])
In [3]: np.ones((4, 2))
Out[3]:
array([[ 1., 1.],
[ 1., 1.],
[ 1., 1.],
[ 1., 1.]])
In [4]: np.zeros((4, 2))
Out[4]:
array([[ 0., 0.],
[ 0., 0.],
[ 0., 0.],
[ 0., 0.]])
In [5]: np.repeat([7], 10)
Out[5]: array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])
Python containers contain references to other objects. See this example:
>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]
In this b
is a list that contains one item that is a reference to list a
. The list a
is mutable.
The multiplication of a list by an integer is equivalent to adding the list to itself multiple times (see common sequence operations). So continuing with the example:
>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]
We can see that the list c
now contains two references to list a
which is equivalent to c = b * 2
.
Python FAQ also contains explanation of this behavior: How do I create a multidimensional list?
myList = [[1]*4] * 3
creates one list object [1,1,1,1]
in memory and copies its reference 3 times over. This is equivalent to obj = [1,1,1,1]; myList = [obj]*3
. Any modification to obj
will be reflected at three places, wherever obj
is referenced in the list. The right statement would be:
myList = [[1]*4 for _ in range(3)]
or
myList = [[1 for __ in range(4)] for _ in range(3)]
Important thing to note here is that *
operator is mostly used to create a list of literals. Although 1
is immutable, obj =[1]*4
will still create a list of 1
repeated 4 times over to form [1,1,1,1]
. But if any reference to an immutable object is made, the object is overwritten with a new one.
This means if we do obj[1]=42
, then obj
will become [1,42,1,1]
not
as some may assume. This can also be verified:[42,42,42,42]
>>> myList = [1]*4
>>> myList
[1, 1, 1, 1]
>>> id(myList[0])
4522139440
>>> id(myList[1]) # Same as myList[0]
4522139440
>>> myList[1] = 42 # Since myList[1] is immutable, this operation overwrites myList[1] with a new object changing its id.
>>> myList
[1, 42, 1, 1]
>>> id(myList[0])
4522139440
>>> id(myList[1]) # id changed
4522140752
>>> id(myList[2]) # id still same as myList[0], still referring to value `1`.
4522139440
Let us rewrite your code in the following way:
x = 1
y = [x]
z = y * 4
myList = [z] * 3
Then having this, run the following code to make everything more clear. What the code does is basically print the ids of the obtained objects, which
Return the “identity” of an object
and will help us identify them and analyse what happens:
print("myList:")
for i, subList in enumerate(myList):
print("\t[{}]: {}".format(i, id(subList)))
for j, elem in enumerate(subList):
print("\t\t[{}]: {}".format(j, id(elem)))
And you will get the following output:
x: 1
y: [1]
z: [1, 1, 1, 1]
myList:
[0]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
[1]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
[2]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
So now let us go step-by-step. You have x
which is 1
, and a single element list y
containing x
. Your first step is y * 4
which will get you a new list z
, which is basically [x, x, x, x]
, i.e. it creates a new list which will have 4 elements, which are references to the initial x
object. The net step is pretty similar. You basically do z * 3
, which is [[x, x, x, x]] * 3
and returns [[x, x, x, x], [x, x, x, x], [x, x, x, x]]
, for the same reason as for the first step.
In simple words this is happening because in python everything works by reference, so when you create a list of list that way you basically end up with such problems.
To solve your issue you can do either one of them: 1. Use numpy array documentation for numpy.empty 2. Append the list as you get to a list. 3. You can also use dictionary if you want
I guess everybody explain what is happening. I suggest one way to solve it:
myList = [[1 for i in range(4)] for j in range(3)]
myList[0][0] = 5
print myList
And then you have:
[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
@spelchekr from Python list multiplication: [[...]]*3 makes 3 lists which mirror each other when modified and I had the same question about "Why does only the outer *3 create more references while the inner one doesn't? Why isn't it all 1s?"
li = [0] * 3
print([id(v) for v in li]) # [140724141863728, 140724141863728, 140724141863728]
li[0] = 1
print([id(v) for v in li]) # [140724141863760, 140724141863728, 140724141863728]
print(id(0)) # 140724141863728
print(id(1)) # 140724141863760
print(li) # [1, 0, 0]
ma = [[0]*3] * 3 # mainly discuss inner & outer *3 here
print([id(li) for li in ma]) # [1987013355080, 1987013355080, 1987013355080]
ma[0][0] = 1
print([id(li) for li in ma]) # [1987013355080, 1987013355080, 1987013355080]
print(ma) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
Here is my explanation after trying the code above:
- The inner
*3
also creates references, but it's references are immutable, something like[&0, &0, &0]
, then when to changeli[0]
, you can't change any underlying reference of const int0
, so you can just change the reference address into the new one&1
; - while
ma=[&li, &li, &li]
andli
is mutable, so when you callma[0][0]=1
, ma[0][0] is equally to&li[0]
, so all the&li
instances will change its 1st address into&1
.
Trying to explain it more descriptively,
Operation 1:
x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]
x[0][0] = 1
print(x) # [[1, 0], [0, 0]]
Operation 2:
y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]
y[0][0] = 1
print(y) # [[1, 0], [1, 0]]
Noticed why doesn't modifying the first element of the first list didn't modify the second element of each list? That's because [0] * 2
really is a list of two numbers, and a reference to 0 cannot be modified.
If you want to create clone copies, try Operation 3:
import copy
y = [0] * 2
print(y) # [0, 0]
y = [y, copy.deepcopy(y)]
print(y) # [[0, 0], [0, 0]]
y[0][0] = 1
print(y) # [[1, 0], [0, 0]]
another interesting way to create clone copies, Operation 4:
import copy
y = [0] * 2
print(y) # [0, 0]
y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]
y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]
By using the inbuilt list function you can do like this
a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list
a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number
a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting
a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list
These questions have a lot of answers, I am adding my answer to explain the same diagrammatically.
The way you created the 2D, creates a shallow list
arr = [[0]*cols]*row
Instead, if you want to update the elements of the list, you should use
rows, cols = (5, 5)
arr = [[0 for i in range(cols)] for j in range(rows)]
Explanation:
One can create a list using :
arr = [0]*N
or
arr = [0 for i in range(N)]
In the first case all the indices of the array point to the same integer object
and when you assign a value to a particular index, a new int object is created, for eg arr[4] = 5
creates
Now let us see what happens when we create a list of list, in this case, all the elements of our top list will point to the same list
And if you update the value of any index a new int object will be created. But since all the top-level list indexes are pointing at the same list, all the rows will look the same. And you will get the feeling that updating an element is updating all the elements in that column.
Credits: Thanks to Pranav Devarakonda for the easy explanation here
I arrived here because I was looking to see how I could nest an arbitrary number of lists. There are a lot of explanations and specific examples above, but you can generalize N dimensional list of lists of lists of ... with the following recursive function:
import copy
def list_ndim(dim, el=None, init=None):
if init is None:
init = el
if len(dim)> 1:
return list_ndim(dim[0:-1], None, [copy.copy(init) for x in range(dim[-1])])
return [copy.deepcopy(init) for x in range(dim[0])]
You make your first call to the function like this:
dim = (3,5,2)
el = 1.0
l = list_ndim(dim, el)
where (3,5,2)
is a tuple of the dimensions of the structure (similar to numpy shape
argument), and 1.0
is the element you want the structure to be initialized with (works with None as well). Note that the init
argument is only provided by the recursive call to carry forward the nested child lists
output of above:
[[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]]]
set specific elements:
l[1][3][1] = 56
l[2][2][0] = 36.0+0.0j
l[0][1][0] = 'abc'
resulting output:
[[[1.0, 1.0], ['abc', 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 56.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 1.0], [(36+0j), 1.0], [1.0, 1.0], [1.0, 1.0]]]
the non-typed nature of lists is demonstrated above
Note that items in the sequence are not copied; they are referenced multiple times. This often haunts new Python programmers; consider:
>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]
What has happened is that [[]]
is a one-element list containing an empty list, so all three elements of [[]] * 3
are references to this single empty list. Modifying any of the elements of lists modifies this single list.
Another example to explain this is using multi-dimensional arrays.
You probably tried to make a multidimensional array like this:
>>> A = [[**None**] * 2] * 3
This looks correct if you print it:
>>> A
[[None, None], [None, None], [None, None]]
But when you assign a value, it shows up in multiple places:
>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]
The reason is that replicating a list with *
doesn’t create copies, it only creates references to the existing objects. The 3 creates a list containing 3 references to the same list of length two. Changes to one row will show in all rows, which is almost certainly not what you want.