Python list problem [duplicate] - python

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

Related

autofilling python list of lists [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 5 years ago.
When I create and fill a list of lists some odd behaviour pops up:
list1 = [[0,0,0],[0,0,0],[0,0,0]]
list2 = [[0]*3]*3
print('lists still look equal here:')
print(list1)
print(list2)
list1[1].pop(1)
list2[1].pop(1)
print('but not anymore:')
print(list1)
print(list2)
gives me this output:
lists look completely equal here:
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
but not anymore:
[[0, 0, 0], [0, 0], [0, 0, 0]]
[[0, 0], [0, 0], [0, 0]]
So the second list 'pops' from every little list instead of just the one I'm trying to. I wonder what causes this behaviour and if there is a more elegant way to fill indexable lists if I need a large amount of long lists instead of just these tiny ones?
When using the operator * it means the items are pointing to the same memory location.
Therefore when poping from the first list only the first item is removed.
And when poping from the second list, as each of the elements in the list pointing to the same memory location, all elements get affected.
Take a look at this:
list1 = [[0,0,0],[0,0,0],[0,0,0]]
list2 = [[0]*3]*3
for elem in list1:
print (id(elem))
print ('--------')
for elem in list2:
print (id(elem))
Output:
32969912
32937024
32970192
--------
32970752
32970752
32970752
As you can see, each element in the second list has the same id.
This is quite a common mistake. In the second definition:
list2 = [[0] * 3]*3
the three sub-lists share the same reference in memory, so if you pop from one, you pop from "all" of them, because all three of them point to the same object.
To avoid this, use:
list2 = [[0] * 3 for _ in range(3)]
which will generate different three lists.

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

Trouble cloning a matrix [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.
def add_column(matrix):
"""
>>> m = [[0, 0], [0, 0]]
>>> add_column(m)
[[0, 0, 0], [0, 0, 0]]
>>> n = [[3, 2], [5, 1], [4, 7]]
>>> add_column(n)
[[3, 2, 0], [5, 1, 0], [4, 7, 0]]
>>> n
[[3, 2], [5, 1], [4, 7]]
"""
new_matrix = matrix[:]
for row in new_matrix:
row += [0]
return new_matrix
Doctests to return the original matrix will return the new matrix, I'm not sure how to clone this matrix without editing the original.
You are making a copy of the outer list, but each inner list is still the same. You need to use a different list for row instead of modifying it:
new_matrix = []
for row in matrix:
new_matrix.append(row + [0])
A condensed version of that is:
new_matrix = [row + [0] for row in matrix]
The problem arises because the matrix is represented as a list of lists. The statement new_matrix = matrix[:] makes a copy of the "outer" list but does not make a copy of each row. Your implementation is also known as a shallow copy. See this post for a more detailed explanation.
You can either implement the add_column method following zondo's suggestion, use the copy module to create a deep copy, or use numpy. Although using numpy requires you to get to know a new library, the flexibility and computational efficiency is worth it in the long run.

Python array strange behavior? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 6 years ago.
Can anyone explain why is this happening?
Case 1:
>>> a = [[0]] *3
>>> print a
[[0], [0], [0]]
>>> a[1].append(0)
>>> print a
[[0, 0], [0, 0], [0, 0]]
Case 2:
>>> a = [[0],[0],[0]]
>>> print a
[[0], [0], [0]]
>>> a[1].append(0)
>>> print a
[[0], [0, 0], [0]]
Why is this going on? I am expecting that the behavior of the array in case 1 to be as in case 2 but it is not for some reason.
In the first case, the three elements in a actually reference to the same list objects. You can check their id:
>>> id(a[0])
4524132472
>>> id(a[1])
4524132472
>>> id(a[2])
4524132472
In the first case, you are creating a list [0] and duplicate it 3 times. This is the same object repeated three times. You should use the star form only with immutable type
To avoid this issue when you have a mutable type, use list comprehension:
a = [[0] for _ in range(3)]

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

Categories