Problem with matrices in Python [duplicate] - python

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)]

Related

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

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]

Value in list changed after running through loop

I´ve been cleaning some data recently and there is just one thing that struck me.
Simple example:
test_list1 = [[1,2,3,4,5], [1,2,3,4,5]]
for x in test_list1:
for y in range(0, len(x)):
x[y] = 0
print(test_list1)
-> [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
However, if I try the following, I obtain a different result:
test_list2 = [1,2,3,4,5]
for x in test_list2:
x = 0
print(test_list2)
-> [1, 2, 3, 4, 5]
It seems rather odd to me that in test_list1, I was able to change the values in its sub-lists without actually referring to test_list1.
Why did the values in test_list1 change just by running the loop if I didn´t explicitly stated that test_list1[0][0] = 0 and so on?
In test_list2, it was not possible.
Thanks in advance
Your question can be simplified to this:
a = 0
b = a
b = 1
print(a, b) # >> 0 1
a = [0,0]
b = a
b[0] = 1
print(a, b) # >> [1, 0] [1, 0]
That is because when you do b = a with numbers, b is a copy of a, so you can edit them separately. But when you do this with a list, the two variables correspond to the same object. This is mostly done to improve efficiency (copying a list every time you do something like a = b would be very inefficient and often useless). So when you edit one, the other one is affected.
In for x in test_list2:, x is a copy of an element of test_list2. But in for x in test_list1:, x directly correspond to an element (a list) of test_list2.
For more details, you can read this article about mutable objects.
I think the bottom line is that in your first loop, each instance of x is a list. In your second loop, each instance of x is an integer. In Python, lists are mutable, but ints are immutable. If you do a search online for "python mutable vs immutable", you can find a number of pages that describe what the difference is and why it leads to results like this.

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 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).

Python list problem [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 5 years ago.
python:
m=[[0]*3]*2
for i in range(3):
m[0][i]=1
print m
I expect that this code should print
[[1, 1, 1], [0, 0, 0]]
but it prints
[[1, 1, 1], [1, 1, 1]]
This is by design. When you use multiplication on elements of a list, you are reproducing the references.
See the section "List creation shortcuts" on the Python Programming/Lists wikibook which goes into detail on the issues with list references to mutable objects.
Their recommended workaround is a list comprehension:
>>> s = [[0]*3 for i in range(2)]
>>> s
[[0, 0, 0], [0, 0, 0]]
>>> s[0][1] = 1
>>> s
[[0, 1, 0], [0, 0, 0]]
This is a bit devilish, but quite obvious when you understand what you're doing. when you're doing the [[0]*3]*2 bit, you're first creating a list with 3 zeros, then you copy that to make two elements. But when you do that copy, you do not create new lists with the same contents, but rather reference the same list several times. So when you change one, they all change.
An example to highlight it:
In [49]: s = [[]]*2 # Create two empty lists
In [50]: s # See:
Out[50]: [[], []]
In [51]: s[0].append(2) # Alter the first element (or so we think)
In [52]: s # OH MY, they both changed! (because they're the same list!)
Out[52]: [[2], [2]]

Categories