Related
This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 2 years ago.
Let's take the following code
board = [[0,0,0],[0,0,0],[0,0,0]]
versionsOfBoard = []
versionsOfBoard.append(board); #this will keep the versions of board array
print(versionsOfBoard);
# output
# [[[0,0,0],[0,0,0],[0,0,0]]]
# later I change few values in the board and append 'board' to my versions array
board[0][0] = 1
versionsOfBoard.append(board)
print(versionsOfBoard)
# output
# [[[1, 0, 0], [0, 0, 0], [0, 0, 0]], [[1, 0, 0], [0, 0, 0], [0, 0, 0]]]
# if you notice here, the 0th index value is also changed
board[1][2] = 1
versionsOfBoard.append(board)
print(versionsOfBoard)
# output
# [[[1, 0, 0], [0, 0, 1], [0, 0, 0]], [[1, 0, 0], [0, 0, 1], [0, 0, 0]], [[1, 0, 0], [0, 0, 1], [0, 0, 0]]]
Every index value displays the latest state of 'board'. it is referencing the board.
I have tried with .append(board[:]) and .append(board.copy()). Both these options too do not solve the problem.
Any help would be highly appreciated
Try this
import copy
board = [[0,0,0],[0,0,0],[0,0,0]]
versionsOfBoard = []
versionsOfBoard.append(copy.deepcopy(board))
print(versionsOfBoard);
board[0][0] = 1
versionsOfBoard.append(copy.deepcopy(board))
print(versionsOfBoard)
board[1][2] = 1
versionsOfBoard.append(copy.deepcopy(board))
print(versionsOfBoard)
This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 5 years ago.
I recently tried to instantiate a 4x4 constant (0's) array by using
a = [[0] * 4] * 4
which instantiates the array a as
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
However, this does not create an array of unique elements as altering an element in any of the arrays changes all of them, e.g.:
a[0][0] = 1
alters a to
[[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 0, 0, 0]]
I think I understand why this happens (copies of lists copy the list's pointer and do not create separate copies unless specified, unlike int's, etc.) but am left wondering:
Is there any quick and easy way to instantiate a constant array (without using any external modules, such as NumPy) with unique elements that can later be altered by simple a[i][j] = x addressing?
a = [[0 for _ in xrange(4)] for _ in xrange(4)]
should do it, it'll create separate lists
Just for free. What is going on here ? When one does
>>> a = [[0] * 4] * 4
first, one creates one list [0] * 4 with four 0 elements. Let call this list li.
Then when you do [li] * 4, one actually creates a list which refers four times to the same object. See
>>> [id(el) for el in a]
[8696976, 8696976, 8696976, 8696976] # in my case
Whence the (not that) curious result one gets when entry-wise assigning like so
>>> a[0][0] = 1
[[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 0, 0, 0]]
A solution is simply to ensure that each element of the list really is unique. For example doing
#Python2
>>> a = map(lambda _: [0]*4, range(4))
#Python3
>>> a = list(map(lambda _: [0]*4, range(4)))
#Python2&3
>>> a[0][0] = 1
[[1, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
I've been given the pseudo-code:
for i= 1 to 3
for j = 1 to 3
board [i] [j] = 0
next j
next i
How would I create this in python?
(The idea is to create a 3 by 3 array with all of the elements set to 0 using a for loop).
If you really want to use for-loops:
>>> board = []
>>> for i in range(3):
... board.append([])
... for j in range(3):
... board[i].append(0)
...
>>> board
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
But Python makes this easier for you:
>>> board = [[0]*3 for _ in range(3)]
>>> board
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
arr=[[0,0,0] for i in range(3)] # create a list with 3 sublists containing [0,0,0]
arr
Out[1]: [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
If you want an list with 5 sublists containing 4 0's:
In [29]: arr=[[0,0,0,0] for i in range(5)]
In [30]: arr
Out[30]:
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
The range specifies how many sublists you want, ranges start at 0, so ranges 4 is 0,1,2,3,4.
gives you five [0,0,0,0]
Using the list comprehension is the same as:
arr=[]
for i in range(5):
arr.append([0,0,0,0])
arr
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
If you want something closer to your pseudocode:
board = []
for i in range(3):
board.append([])
for j in range(3):
board[i].append(0)
numpy has something for this:
numpy.zeros((3,3))
You can use the style of pseudocode given or simply just use a python one liner
chess_board = [[x]*3 for _ in range(y)] --> list comprehension
or you can use the plain loop style of other languages like java. I prefer the one liner as it looks much nicer and cleaner.
What is wrong with the following program code, attempting to initialize a 4 x 4 matrix of integers? How should the initialization be done?
line = [0] * 4
matrix = [line, line, line, line]
Use a list comprehension:
>>> line = [[0]*4 for _ in xrange(4)]
>>> line
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Don't do this though:
>>> line = [[0]*4]*4
>>> line
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
The output looks same, but the problem here is all inner lists are actually the same object repeated 4 times:
>>> [id(x) for x in line]
[156931756, 156931756, 156931756, 156931756]
So, changing one of them is going to affect all:
>>> line[2][0] = 10
>>> line
[[10, 0, 0, 0], [10, 0, 0, 0], [10, 0, 0, 0], [10, 0, 0, 0]]
Same thing is applicable to your code:
>>> line = [0] * 4
>>> matrix = [line, line, line, line]
>>> [id(x) for x in matrix]
[158521804, 158521804, 158521804, 158521804]
If line contains only immutable object then you can change your code do:
>>> matrix = [line[:] for _ in xrange(4)]
But, if line contains mutable objects itself, then you'd have to use either copy.deepcopy or better create a new line object inside the list comprehension.
you could use numpy for that if you want to perform computations on your matrix
import numpy as np
zeros = np.zeros([4,4])
The problem is:
>>> line = [0] * 4
>>> matrix = [line, line, line, line]
>>> matrix[0][0] = 5
>>> matrix
[[5, 0, 0, 0], [5, 0, 0, 0], [5, 0, 0, 0], [5, 0, 0, 0]]
You have and array of references to the same vector.
What is wrong here that you create a list of 4 references to line list. If you change any of sub-lists or line itself, you affect all sub-lists of matrix, since they (sub-lists) are essentially the same list
Here is a little demonstration
In [108]: line = [0] * 4
In [109]: matrix = [line, line, line, line]
In [110]: line[1]=2
In [111]: matrix
Out[111]: [[0, 2, 0, 0], [0, 2, 0, 0], [0, 2, 0, 0], [0, 2, 0, 0]]
In [112]: matrix[1][3] = 4
In [113]: matrix
Out[113]: [[0, 2, 0, 4], [0, 2, 0, 4], [0, 2, 0, 4], [0, 2, 0, 4]]
In [114]: for row in matrix:
.....: print id(row)
.....:
3065415660
3065415660
3065415660
3065415660
You need to do this:
matrix = [[0 for row in range(4)] for col in range(4)]
Your code works ok, but it's not flexible one. So, if you want to create 5*5 martix you have explicitly add more line objects to matrix init code. So, using for + xrange generators looks more suitable.
Also, not sure about case of using this matrix - but be aware about using the same object (list) as matrix line. So, if you change it's element - it would be modified in all rows:
matrix[0][0] = 'new value'
print matrix
[['new value', 0, 0, 0], ['new value', 0, 0, 0], ['new value', 0, 0, 0], ['new value', 0, 0, 0]]
I have been beating my head against a wall over this problem. I create a list and make 4 copies, only one of which shares the same memory index. If I change the original list, is somehow changes 3 of those copies as well, 2 of which have a different memory index. Only if I make a list using the same command as the original, am I able to create a list that is not impacted by changes to the original. How is this possible? Here is the output from my console:
>>> orig=[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
>>> id(orig)
151498220
>>> copy1=orig #same index
>>> id(copy1)
151498220
>>> copy2=orig[:] #different index
>>> id(copy2)
151498348
>>> copy3=list(orig) #different index
>>> id(copy3)
151503020
>>> copy4=[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
>>> id(copy4)
151498636
>>> orig[0][1]=34
>>> copy1
[[0, 34, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]] #expected, same memory index
>>> copy2
[[0, 34, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]] #WTF?!?!?
>>> copy3
[[0, 34, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]] #ARGH!!!
>>> copy4
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]] #workaround?
>>> id(orig)
151498220
>>> id(copy1)
151498220
>>> id(copy2)
151498348
>>> id(copy3)
151503020
>>> id(copy4)
151498636
The memory indices did not change and yet the lists were altered. Only copy1 should have changed as it has the same memory index as orig.
That's because you are just creating a shallow copy. You need to create a deep copy instead.
As per copy module doc:
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in
the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
You can verify it by comparing the id of inner list:
>>> orig=[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
>>> id(orig)
151498220
>>> copy2=orig[:] #different index
>>> id(copy2)
151498348
>>> id(copy2[0]) == id(orig[0]) # inner list have same id
True
You can create a deepcopy using copy.deepcopy(x):
>>> import copy
>>>
>>> copy3 = copy.deepcopy(orig)
>>>
>>> id(copy3[0]) == id(orig[0]) # inner list have different id
False
>>> orig[0][3] = 34
>>>
>>> orig
[[0, 34, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
>>> copy3
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
This is because while the outer lists (copy1, copy2, etc.) are separate objects, their sublists are all references to the same lists you created in orig. Try doing:
id(orig[0]) == id(copy3[0]) #should return True
In order to make a deep copy of the list object and all the objects it references, use deepcopy.
Your list is a list of names, not a list of lists as you are thinking of it. When you make a copy of the list using any of the methods you list (slicing, creating a new list based on the old one, etc) you make a new outer list, but the names in the new list reference the same internal lists as the names in the old one.
# One through three are all examples of:
first_list, second_list, third_list = [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]
original = [first_list, second_list, third_list]
another_list = original[:]
# We do indeed have another list
assert id(original) != id(another_list)
# But the *references* in the list are pointing at the same underlying child list
assert id(original[0]) == id(another_list[0])