Issue while modifying elements of nested list in Python - python

In this code, I was expecting that list A will have the same value even if I assign it to another temp variable and then print the argument A using the function foo. However, if A was a scalar e.g. A=3 then the value of A remains the same even after calling foo.
Where am I going wrong? Is there a problem in the scope of variables? I found some related Strange behavior of lists in python answer but couldn't figure out a fix for my problem.
A = [ [ 0 for i in range(3) ] for j in range(3) ]
def foo(input):
temp= input
temp[0][0]=12
print(input)
print(A)
answer = foo(A)
Output:
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[12, 0, 0], [0, 0, 0], [0, 0, 0]]

instead of input, use the .copy this will make a copy of the array and assign new addresses to temp, what you do is shallow copying, where temp = input, just copies the address of the input array to temp, rather than making a copy of the list.
So you may either do foo(A.copy()) or temp=input.copy() also note input is not a good name since it is already assigned to a python function, use something like foo_arg or something

Please this, I used deepcopy
from copy import copy, deepcopy
A = [ [ 0 for i in range(3) ] for j in range(3) ]
def foo(input):
temp = deepcopy(input)
temp[0][0]=12
return temp
print('origin', A)
answer = foo(A)
print('after', A)
print('result', answer)
Result:
origin [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
after [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
result [[12, 0, 0], [0, 0, 0], [0, 0, 0]]

Related

Why I can't make a two dimensional array with this code?

I was trying to solve a problem but I encounter a problem.
When I do this:
arr=[[[0]*5]*5]
I get this:
[[[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]]]
But when I try to select an element:
arr[0][0]
It returns this:
[0, 0, 0, 0, 0]
Why?
Simply remove the outer []:
arr=[[0]*5]*5
However that's a bad way to create nested lists. It's better to use:
arr = [[0 for _ in range(5)] for _ in range(5)]
That way you don't get bitten by the "shared reference problem".
arr = [[0]*5]*5 #will work
so arr = [[[0]*5]*5] is equivalent to arr = [your required arr] inside another list
You have an extra pair of square brackets. Try arr = [[0]*5]*5.
You have an extra pair of brackets.
Try instead:
arr = [[0] * 5] * 5
We can prove this by breaking down your code:
arr= [ [ [ 0 ] * 5 ] * 5]
We condense the innermost layer to be: arr = [ [ [0]*5 ] * 5] which returns:
[ [[0, 0, 0, 0, 0]] * 5]
Now the inner portion returns:
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
As your desired result is now present, we now see that the remaining brackets are no longer needed.
because you're not getting
[[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]]
you're getting
[[[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]]]
omit the extra brackets.
arr=[[0]*5]*5

why the new_list has to be put inside the for loop to prevent global change?

Would any one tell me the difference between these two blocks? I have hard time figuring out why the second one only changes the nested list locally, while the first changes it globally. it seems to me they do the same thing.
my_list = [ ]
new_list = [0, 0, 0 ]## outside the loop
for index in range(5):
my_list.append(new_list)
my_list[0][1] = 5
print(my_list)
## result
[[0, 5, 0], [0, 5, 0], [0, 5, 0], [0, 5, 0], [0, 5, 0]]
my_list = [ ]
for index in range(5):
new_list = [0, 0, 0 ] ## inside the loop
my_list.append(new_list)
my_list[0][1] = 5
print(my_list)
## result
[[0, 5, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
Because inside the for loop, new_list get's redefined, effectively refering to a different list object for every iteration.
Printing the ids for the second case reveals this:
print(*map(id, my_list)) # Notice the different ids
140609203176456 140609194670088 140609194608840 140609212158216 140609194670152
Outside the loop, you append the same list object, the same reference. Changes will be visible througout all references.
Printing the ids for this case shows the same id (i.e, same list) is present:
print(*map(id, my_list)) # same id.
140609194670088 140609194670088 140609194670088 140609194670088 140609194670088

Insert value into the element of 2D array

Supposed I created the 2D array of 3x4 with zeros initialization.
Then, I want to add 100 into arr[0][1] and I expect that 100 is added Only into arr[0][1] cell. Refer to Case 1 and Case 2:
From Case 1:
what I saw is that 100 is added into every row with column 1 of arr. It is not what i expected.
From Case 2
The result is what i have expected. Can anyone help me explain?
Case 1:
>>> arr = [[0]*4]*3
>>> arr
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> len(arr)
3
>>> len(arr[0])
4
>>> type(arr)
<class 'list'>
>>> arr[0] [1] = 100
>>> arr
[[0, 100, 0, 0], [0, 100, 0, 0], [0, 100, 0, 0]]
Case 2:
>>> arr = [[0]*4 for _ in range(3)]
>>> arr
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> type(arr)
<class 'list'>
>>> arr[0] [1] = 100
>>> arr
[[0, 100, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
[0]*4 creates a list of references to the same object 0. Since integers are immutable, that's OK; changing one element replaces it with a new object. However, [[0]*4]*3] creates a list with 3 references to the same, mutable list object that contains 4 references to 0. Changing an element of one list, as you've seen, shows up as a change in all 3 references.
In the list comprehension, a new list [0]*4 is created for each value of _, rather than simply storing a new reference to the same list.

Nested List Indices [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 9 years ago.
I have experienced some problem by using a nested list in Python in the code shown bleow.
Basically, I have a 2D list contains all 0 values, I want to update the list value in a loop.
However, Python does not produce the result I want. Is there something that I misunderstand about range() and Python list indices?
some_list = 4 * [(4 * [0])]
for i in range(3):
for j in range(3):
some_list[i+1][j+1] = 1
for i in range(4):
print(some_list[i])
The results I expected are:
[0, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
But the actual results from Python are:
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
What's going on here?
The problem is caused by the fact that python chooses to pass lists around by reference.
Normally variables are passed "by value", so they operate independently:
>>> a = 1
>>> b = a
>>> a = 2
>>> print b
1
But since lists might get pretty large, rather than shifting the whole list around memory, Python chooses to just use a reference ('pointer' in C terms). If you assign one to another variable, you assign just the reference to it. This means that you can have two variables pointing to the same list in memory:
>>> a = [1]
>>> b = a
>>> a[0] = 2
>>> print b
[2]
So, in your first line of code you have 4 * [0]. Now [0] is a pointer to the value 0 in memory, and when you multiply it, you get four pointers to the same place in memory. BUT when you change one of the values then Python knows that the pointer needs to change to point to the new value:
>>> a = 4 * [0]
>>> a
[0, 0, 0, 0]
>>> [id(v) for v in a]
[33302480, 33302480, 33302480, 33302480]
>>> a[0] = 1
>>> a
[1, 0, 0, 0]
The problem comes when you multiply this list - you get four copies of the list pointer. Now when you change one of the values in one list, all four change together:
>>> a[0][0] = 1
>>> a
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]
The solution is to avoid the second multiplication. A loop does the job:
>>> some_list = [(4 * [0]) for _ in range(4)]
Actually all the objects in your list are same, so changing one changes others too:
In [151]: some_list = 4 * [(4 * [0])]
In [152]: [id(x) for x in some_list]
Out[152]: [148641452, 148641452, 148641452, 148641452]
In [160]: some_list[0][1]=5 #you think you changed the list at index 0 here
In [161]: some_list
Out[161]: [[0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0]] #but all lists are changed
Create your list this way:
In [156]: some_list=[[0]*4 for _ in range(4)]
In [157]: some_list
Out[157]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
In [158]: [id(x) for x in some_list]
Out[158]: [148255436, 148695180, 148258380, 148255852]
In [163]: some_list[0][1]=5
In [164]: some_list
Out[164]: [[0, 5, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] #works fine in this case

Initialise matrix in python [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Python list problem
I try to initialise a matrix in python.
First I did this:
>>> M=[[0]*4]*4
But here is my probleme, every line is changing when I change the first one:
>>> M
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> M[1][1]=1
>>> M
[[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]]
So I did it this way:
>>> M= [ [ 0 for i in range(4) ] for j in range(4) ]
And ut works fine:
>>> M
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> M[1][1]=1
>>> M
[[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
My question is:
What these two expressions really mean ? ans why the first one is behaving this way ?
Thanks in advance for you help.
When you multiply those lists, Python is copying them by reference rather than creating entirely new objects.
A simple example might help, showing what happens with copy by reference:
>>> pie = ['apple', 'cherry', 'pecan']
>>> pie_copy = pie
>>> pie_copy[0] = 'banana'
>>> pie
['banana', 'cherry', 'pecan']
>>> pie is pie_copy
True
>>> new_pie = ['banana', 'cherry', 'pecan']
>>> pie is new_pie
False
In the same way that pie_copy and pie point to the same list, when building lists by multiplying, all the copies point to the same list.
In your second snippet using range() and list comprehensions, you aren't taking a single list and copying it several times; each iteration in the comprehension is creating a new list, so you don't suffer from the same copy by reference problem.
Because here M=[[0]*4]*4
You create a links on objects.
It's the similar as
>>> a = [0, 0, 0]
>>> b = [a,a,a]
>>> b
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[1] = 1
>>> b
[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
>>>
UPD links I meant references, sorry if little confusing
Say a is some python object. Then [a] * 4 is equivalent to [a, a, a, a]. What this means depends on whether a is mutable or not. Numbers, strings and tuples are not, so if a is one of these kinds of objects (0 in your example), then you get 4 independently changeable copies. Lists, dictionaries and sets are mutable, and in this case you just get 4 references to the same object, in your case the list [0] * 4. Exploiting this knowledge, you'll see that you can do this:
M = [[0] * 4 for i in range(4)]
and get what you want.

Categories

Resources