two dimensional python array manipulation does no yield desired result [duplicate] - python

This question already has answers here:
Initializing 2D array in Python
(4 answers)
Closed 2 years ago.
I am stumped by this silly and simple issue. Just can't understand what is wrong here!
a = [[0]*3]*2
print(a)
a[1][2] = 1
print(a)
i get the following output
[[0,0,0],[0,0,0]]
[[0,0,1],[0,0,1]]
I don't understand why I see two 1, while I changed only one of them. How do I change only one of them?

by doing
a = [[0]*3]
You created an array
[0,0,0]
You then copied the reference to this array when you did
a = [[0]*3]*2
As a result changing one causes the other to change. This is known as a shallow copy.
What you want is a deepcopy and can be achieved by the copy's library deepcopy() method
Hence, do this instead:
import copy
a = [[0]*3]
a += copy.deepcopy(a)
a[1][2] = 1
print(a)
[[0, 0, 0], [0, 0, 1]]

It's because the [[0]*3]*2 is creating two smaller lists that are both associated to the same data value (because of the *2). To fix this, you would just want to define it in a different way that doesn't do this, for example:
a = [[0,0,0],[0,0,0]]
or
a = [[0]*3] + [[0]*3]

Related

Strange error when altering 2 dimensional lists in Python [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 3 years ago.
I've tried to make some code for a game in python 3.6.3 and when I try to change a value in a 2d list/array (in a list inside a list), it changes the values of that index in all the lists. Does anybody know why that's the case?
I've abstracted the problem to the exact part of the code that's going wrong. I've tried using different numbers and formats but the problem remains. Debugging tools don't help either.
#Strange error
list_1 = [0,0]
list_2 = [list_1, list_1, list_1]
list_2[0][1] = 1
print(list_2)
#Working normally
list_3 = [[0,0],[0,0],[0,0]]
list_3[0][1] = 1
print(list_3)
The actual result should be that both lists have the same output of [0, 1],[0, 0], [0, 0]. However the first list returns [0, 1],[0, 1],[0, 1] whereas the second list works normally with [0, 1],[0, 0], [0, 0] returned, even though both lists should return the exact same output.
you're just creating references to list1 3 times in list2
you should do list2 = [list(list1), list(list1), list(list1)], which creates copies of list1 instead of just references to it.

Python strange behavior: "+=" creates aliases [duplicate]

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 6 years ago.
I can't explain the behavior of this code:
n=[[0,0],[0,0]]
m=n.copy()
for i in range(len(m)):
m[i]+=[0]
The output I get is (not what I expected):
>>> m
[[0, 0, 0], [0, 0, 0]]
>>> n
[[0, 0, 0], [0, 0, 0]]
If I type instead:
n=[[0,0],[0,0]]
m=n.copy()
for i in range(len(m)):
m[i]=m[i]+[0]
I get the correct output (which is what I originally expected):
>>> m
[[0, 0, 0], [0, 0, 0]]
>>> n
[[0, 0], [0, 0]]
So, it looks like if I use the "+=" shortcut the two matrices "m" and "n" become aliases. Can somebody please explain why this is happening?.
n.copy() creates a shallow copy, so n[i] and m[i] are already pointing to the same object (although m and n are different).
For lists, x += y is not quite the same thing as x = x + y - the former mutates x directly (it's equivalent to x.extend(y)) while the latter assigns x to a new value.
These two facts combined explain the behaviour.
The difference here is that some_list += some_iterable is effectively the same thing as some_list.extend(some_iterable).
some_list = some_list + [something_else] actually creates a new list out of some_list and [something_else] concatenated together and then assigns that new list back on the left hand side of the = operator.
When you think of it this way, and with the knowledge that after copying, m[idx] is n[idx] for all 0 <= idx < len(m)1, it's easier to see why the += version changes show up in both m and n.
1list.copy() makes a shallow copy -- which means that it only copies references.

Changing one list unexpectedly changes another, too [duplicate]

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 4 years ago.
I have a list of the form
v = [0,0,0,0,0,0,0,0,0]
Somewhere in the code I do
vec=v
vec[5]=5
and this changes both v and vec:
>>> print vec
[0, 0, 0, 0, 0, 5, 0, 0, 0]
>>> print v
[0, 0, 0, 0, 0, 5, 0, 0, 0]
Why does v change at all?
Why does v change at all?
vec and v are both references.
When coding vec = v you assign v address to vec.
Therefore changing data in v will also "change" vec.
If you want to have two different arrays use:
vec = list(v)
Because v is pointed to the same list as vec is in memory.
If you do not want to have that you have to make a
from copy import deepcopy
vec = deepcopy(v)
or
vec = v[:]
Python points both lists in vec = v to the same spot of memory.
To copy a list use vec = v[:]
This might all seem counter-intuitive. Why not make copying the list the default behavior? Consider the situation
def foo():
my_list = some_function()
# Do stuff with my_list
Wouldn't you want my_list to contain the exact same list that was created in some_function and not have the computer spend extra time creating a copy. For large lists copying the data can take some time. Because of this reason, Python does not copy a list upon assignment.
Misc Notes:
If you're familiar with languages that use pointers. Internally, in the resulting assembly language, vec and v are just pointers that reference the address in memory where the list starts.
Other languages have been able to overcome the obstacles I mentioned through the use of copy on write which allows objects to share memory until they are modified. Unfortunately, Python never implemented this.
For other ways of copying a list, or to do a deep copy, see List changes unexpectedly after assignment. Why is this and how can I prevent it?
Run this code and you will understand why variable v changes.
a = [7, 3, 4]
b = a
c = a[:]
b[0] = 10
print 'a: ', a, id(a)
print 'b: ', b, id(b)
print 'c: ', c, id(c)
This code prints the following output on my interpreter:
a: [10, 3, 4] 140619073542552
b: [10, 3, 4] 140619073542552
c: [7, 3, 4] 140619073604136
As you can see, lists a and b point to the same memory location. Whereas, list c is a different memory location altogether. You can say that variables a and b are alias for the same list. Thus, any change done to either variable a or b will be reflected in the other list as well, but not on list c
Hope this helps! :)
you could use
vec=v[:] #but
"Alex Martelli's opinion (at least back in 2007) about this is, that it is a weird syntax and it does not make sense to use it ever. ;) (In his opinion, the next one is more readable)."
vec=list(v)
I mean it was Erez's link... "How to clone or copy a list in Python?"

Problem with matrices in Python [duplicate]

This question already has answers here:
Multiply operator applied to list(data structure)
(2 answers)
Closed 9 years ago.
I was writing a programm in Python (2.5.4), and I realized that my code was not working because of something very unusual. I am going to give an example:
A = [[0]*2]*2
When I print A, I get:
[[0, 0], [0, 0]]
That is ok. But now I want to change the element in the first column and firts row. So I type:
A[0][0] = 1
But when I print A again, I get:
[[1, 0], [1, 0]]
However, I was expecting
[[1, 0], [0, 0]]
This is ruinning all my code. I want to know why this is happening and how I can fix it.
On the other hand, when I type:
B = [[0,0],[0,0]]
And make:
B[0][0] = 1
I get:
[[1, 0], [0, 0]]
This is even stranger! Aren't the two ways of implementing matrices equivalent? What if I wanted a 100x100 matrix with zeros? For this case, with a 2x2 matrix, I can type [[0, 0], [0, 0]]. But that is not a good solution.
This is because your list contains several references to a list.
>>> a = [0]
>>> l = [a,a]
>>> l[0][0] = "A"
>>> l
[['A'], ['A']]
We create a list and binded it to a. We then store two references to a in the list l via l=[a,a]. Then we manipulate one reference to a, and change it's first element to "A". Since a reference refers to a location in memory, my manipulating that reference (either element in l) we change the value in memory, hence affecting all other references to a.
This illustration, depicts the example above. The arrows represent a reference to a. They are the a's in l = [a,a]. When you change one of them, you change the value which they both point to. That interaction could be depicted like this:
We manipulate a via manipulating l[0] (l[0] is a reference to a), as such we can change the first element in a by changing l[0][0] (which would be the same as a[0]) to "A".
A depiction your list [[0]*2]*2 would look like this
"What if you wanted a 100 x 100 matrix of zeros?"
Use a list comprehension:
[[0] * 100 for x in range(100)]

Changing a particular element in a 2-D list in python

I have the following code:
a=[[0]*2]*3
print a
a[1][1]=2
print a
the output is:
[[0,0],[0,0],[0,0]]
[[0,2],[0,2],[0,2]]
Why is it changing all the elements of the list?
Shouldn't the output be:
[[0,0],[0,2],[0,0]]
What needs to be done if I just want to change one of the element?
Thanks for the help!
When you do [0] * 2, this results in the list [0, 0]. When you multiply that by 3, you're creating a list with three references to that list. You probably want to do this instead:
a = [[0] * 2 for _ in range(3)]
I've seen this problem many times and it is frustrating.
When you do this:
a = [[0] * 2] * 3
You create a list [0, 0] and pass its reference thrice to form a. Those cloned lists are just references to an original list object. When you modify one, you modify the original and all the rest get modified as well.
The solution is pretty simple:
a = [[0 for j in range(2)] for i in range(3)]
Instead of passing a list by reference, you create a new one each time.
Why is it changing all the elements of the list?
Because they are all the same element.
What needs to be done if I just want to change one of the element?
You need to make a list that contains three different instances of [0, 0].
Multiplying a list makes a list that refers to the same things multiple times.
Try, for example, [[0] * 2 for _ in range(3)].
The reason there is no problem with [0] * 2 is that the fact that both elements are "the same 0" doesn't matter; 0 is 0, and can't be changed. Writing a[0][0] = 1 replaces the 0, but modifies the [0, 0] containing that 0 (by virtue of replacing the 0).

Categories