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

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.

Related

Unexpected behaviour of python matrix (list of lists), assignment is changing whole column [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 4 months ago.
please help me with a question, cause Im getting mad.
I'm creating a 0-matrix, than tried to changed it's first element value to one, but it changes the whole column instead, and I don't get why:
def id_mtrx(n):
m = [[0]*n]*n
m[0][0]=1
return m
here is output:
[[1, 0], [1, 0]]
while I was expecting:
[[1, 0], [0, 0]]
It looks very simple, what can be wrong?
You are creating multiple references to the same list object instead of creating new list, try:
n = 2
m = [[0 for _ in range(0, n)] for _ in range (0, n)]
m[0][0] = 1
print(m)
output is:
[[1, 0], [0, 0]]

Appending the same list to another one [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 6 years ago.
I have to append the same list to another one more than one time, and then modify only one of them.
I tried
list_a = []
list_b = [0,0,0]
for x in range(3):
list_a.append(list_b)
but the problem is that if I try
list_a[0][0] = 1
it modifies list_a[1][0] and list_a[2][0] also.
How can I avoid that?
Better way to create a list like this if all you want is to create empty list with all 0s is:
my_list = [[0]*3 for _ in range(3)]
Let's verify the result whether it has the same issue or not:
>>> my_list
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> my_list[0][0] = 1
>>> my_list
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]
# ^ ^ ^
# Yipee! value changed only once
For knowing the reason why your code is not working, check: Python list of lists, changes reflected across sublists unexpectedly
Use the following:
list_a = []
list_b = [0,0,0]
for x in range(3):
list_a.append(list_b[:])
You are appending list_b three times, so what you are modifying is the actual list_b object. What you want to do is to make a shallow copy, this be done like this
list_a.append(list(list_b))
or like this
list_a.append(list_b[:])

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

why can't I change only a single element in a nested list in Python [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 4 years ago.
I just met something really strange of Python:
>>> out=[[0]*3]*3
>>> out
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> out[0][1]
0
>>> out[0][1]=9
>>> out
[[0, 9, 0], [0, 9, 0], [0, 9, 0]]
well, obviously, what I want is :
[[0, 9, 0], [0, 0, 0], [0, 0, 0]]
isn't strange? I'm not very familiar with Python, but Python always impresses me with its intuitive behavior. But how it comes up with this?
... and how can I get what I need?
thanks!
Watt
A strange behaviour indeed, but that's only because * operator makes shallow copies, in your case - shallow copies of [0, 0, 0] list. You can use the id() function to make sure that these internal lists are actually the same:
out=[[0]*3]*3
id(out[0])
>>> 140503648365240
id(out[1])
>>> 140503648365240
id(out[2])
>>> 140503648365240
Comprehensions can be used to create different lists as follows:
out = [ [0]*3 for _ in range(3) ]
Using * to duplicate elements in lists is a shallow copy operation, so you will end up with multiple references to the same mutable objects if you use this on a list that contains mutable objects.
Instead, use the following to initialize your nested list:
out = [[0]*3 for _ in range(3)]
You can see that with your method, each entry in out is actually a reference to the same list, which is why you see the behavior that you do:
>>> out = [[0]*3]*3
>>> out[0] is out[1] is out[2]
True

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