modifying one element modifies the whole column in python [duplicate] - python

This question already has answers here:
python lists, appending something to the list changes the entire thing?
(3 answers)
Closed 8 years ago.
x = raw_input("")
y = raw_input("")
a = []
b = []
count = 1
for i in range(0, int(y)):
b.append(count)
count+=1
for i in range(0, int(x)):
a.append(b)
for i in a:
print ""
for j in i:
print j,
a[1][1] = 0
for i in a:
print ""
for j in i:
print j,
a has been created by appending list "b" n time to it
Now when i modify a[1][1] the whole column that is a[0][1] - a[n][1] gets modified to that value
Can anyone explain why this is happening

Every time you append b, you are appending the same list -- not copies of the list, but multiple references to the same object. If you want each row to be a different list, you need to append a new list each time, by doing a.append(b[:]).

That is because by appending b, you are creating pointers to the same objects. Instead, make a copy as follows:
for i in range(0, int(x)):
a.append(b[:])
You can see it working as expected, here

Related

Why does Python list comprehension seem to behave differently than list "multiplication"? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 8 months ago.
Asking out of curiosity. For the sake of making a point I was trying to make a function that returns an "identity matrix" of n dimensions and then printing it in the most concise way.
First I came up with this:
def identity(n):
zeros = [[0 for j in range(n)] for i in range(n)]
for i in range(n):
zeros[i][i] = 1
return zeros
for i in range(5):
print(identity(5)[i])
This works as intended, however then I tried making the syntax shorter by doing this:
def identity(n):
zeros = [[0]*n]*n
for i in range(n):
zeros[i][i] = 1
return zeros
for i in range(5):
print(identity(5)[i])
And this for some reason changes every single element to a one, but I can't seem to figure out why?. This isn't an important question but help is much appreciated!
lists are kept by reference in python.
This means that if you have:
list_a = [1,2,3,4]
list_b = list_a
list_a and list_b are actually pointing to the same object in memory. so if you change an element of list_a:
list_a[2] = 9
then the same element for list_b will change because they are pointing to the same object. i.e. list_a literally equals list_b in every way.
That's what's happening in your code as well.
When you loop through and assign each value then it's as if you were explicitly creating a new list and assigning it to each element of your outter list:
l = []
l.append([1,2,3,4])
l.append([1,2,3,4])
...
but in the second piece of code, it is as if you are repeatedly appending the same value to the list:
l = []
la = [1,2,3,4]
l.append(la)
l.append(la)
It's because list comprehension performs shallow copy on the element.
All elements in zeros are refering to the same list.
Try these and see the results:
n = 4
zeros = [[0]*n]*n
zeros[0] = 1
print(zeros)
n = 4
lst = [0]*n
zeros = [lst]*n
print(zeros)
lst[0] = 1
print(zeros)
You can learn more about difference between shallow and deep copy here.
This is because when you multiply a list with a number, it duplicates it, but it does so by copying the reference of each element.
If you do id(zeros[0][0]) and id(zeros[0][1]) for the second case, you will see that they are the same, i.e. they refer to the same element and modifying one modifies them all.
This is not true for the first case where every 0 is instantiated seperately and has its own id.
Edit:
def zeros(n):
return [[0]*n]*n
x = zeros(5)
for i in range(5):
for j in range(5):
assert id(x[0][0]) == id(x[i][j])

2D array in python changes the whole row when I only want to change 1 item(Is there a way around it?) [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 3 years ago.
I wanted to create a 2D array that would have a number in each cell.
I need to access specific cells and be ablt to change them.
def print_matrix(matrix):
#just to print it nicer
for x in range(0, len(matrix)):
print(matrix[x])
matrix = []
list = []
for x in range(0,10):
list.append(0)
for x in range(0,10):
matrix.append(list)
matrix[1][2] = 9
print_matrix(matrix)
You actually created only one list and added it 10 times to the matrix (by reference).
matrix = []
for x in range(0,10):
row = []
for x in range(0,10):
row.append(0)
matrix.append(row)
Also, it's not advisable to call variables list, for this is a builtin python function.
Use list.copy():
def print_matrix(matrix):
#just to print it nicer
for x in range(0, len(matrix)):
print(matrix[x])
matrix = []
list = []
for x in range(0,10):
list.append(0)
for x in range(0,10):
matrix.append(list.copy())
matrix[1][2] = 9
print_matrix(matrix)
If you don't use copy, matrix simply holds 10 references to the original list. Changing any one of those references simply changes the original list.
When you use .copy(), new lists are created which are copies of the original one, this allows you to change each one independently.
Your problem is that you have list references in each row in your matrix. Check here for a possible solution.

retrieve in list occurrences from text python [duplicate]

This question already has answers here:
Modifying list while iterating [duplicate]
(7 answers)
Closed 5 years ago.
Text = input('please enter your text')
l = [str(x) for x in Text.split()]
count = 0
for item in l:
for i in range(1,len(item)):
if item[i-1] == item[i]:
count +=1
if count <1:
l.remove(item)
count = 0
print (l)
the goal is : if we have a text : 'baaaaah mooh lpo mpoo' to get a list with elements they have 2 successive same characters, in this case ['baaaaah', 'mooh', 'mpoo' ]
the program is working with the mentioned example
if i use this one it's not working : 'hgj kio mpoo'
thank you
(complex)One liner:
>>> def successive_items(s):
return [x for x in s.split() if any(x[i]==x[i-1] for i in range(1,len(x)))]
>>> successive_items('baaaaah mooh lpo mpoo')
['baaaaah', 'mooh', 'mpoo']
>>> successive_items('hgj kio mpoo')
['mpoo']
In case of your code, you should not modify the list you are iterating over. Say, for example, lets have an array:
a = [1,2,3,4,5]
Now, if you iterate and remove elements (inside the loop), you would expect a to be empty. But let's check out:
>>> a = [1,2,3,4,5]
>>> for item in a:
a.remove(item)
>>> a
[2, 4]
See? It is not empty. This is better explained here.
Your program is not working for the second case because of list modification. Here is how:
Initially your list had ['hgj','kio','mpoo'].
After reading the first element you removed hgj. So the list now becomes ['kio','mpoo'].
The loop iterates the 2nd element next, and it gets mpoo (in the modified list);
kio was never read.
This might help:
sentence = input("please enter your text")
words = sentence.split()
answers = []
for each_word in words:
for idx in range(1, len(each_word)):
if each_word[idx] == each_word[idx-1]:
answers.append(each_word)
break
print(answers)
In your code, you are iterating over a list and at the same time you are modifying that very same list(by deleting elements from it), for more explanation see this answer

Why does Python not manipulate this array? [duplicate]

This question already has answers here:
How to modify list entries during for loop?
(10 answers)
Closed 7 years ago.
I have the following code snippet:
d = [[0,558,437,142,45,290], [558,0,232,500,600,523], [437,232,0,408,492,572],
[142,500,408,0,180,197], [45,600,492,180,0,254], [290,523,572,197,254,0]]
for row in d:
for elem in row:
elem = elem * 2
print d[0][1]
print '\n'
Why does it not print the new value, it still prints 558
elem = elem * 2 modifies the value of a variable named elem. This variable has nothing to do with the list other than its initial value came from a list element.
To do what you want, you need indexes into the array. Alternatively, you can use the map() function. However, this will only create a new list with the new values rather than changing the values of the original list.
You are assigning a new value to the variable elem. This does not effect the list. Instead try this:
d = [[0,558,437,142,45,290], [558,0,232,500,600,523],
[437,232,0,408,492,572],
[142,500,408,0,180,197], [45,600,492,180,0,254], [290,523,572,197,254,0]]
for row in d:
for index in range(len(row)):
row[index] *= 2
print d[0][1]
print '\n'
Notice that I use index for the index to the element instead of accessing the element itself.
Aside of the "why" as mentionned in other answers, here is how you can achieve this using list comprehensions:
d = [[0,558,437,142,45,290], [558,0,232,500,600,523], [437,232,0,408,492,572],
[142,500,408,0,180,197], [45,600,492,180,0,254], [290,523,572,197,254,0]]
for i, el in enumerate(d):
d[i] = [row * 2 for row in el]
print(d)
You probably want something like this instead:
double_d = [[elem * 2 for elem in row] for row in d]
Using list comprehensions is more idiomatic Python.

Index out of range bug [duplicate]

This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
I am trying to delete duplicate elements in the list. The code below says index out of range when the value of i goes to 9. 9 + 1 = 10 is not outside the list and I can print it, but whenever I use del it gives me error. This is the code that doesn't work:
a = [12,12,34,34,56,11,32,32,11,10,10]
g = len(a)
for i in range(0,g-1):
if a[i] == a[i+1]:
del a[i]
print a
However the reverse logic which is I having value from 10 - 1 works but i from 0 - 9 doesn't work. This one works the reverse logic.
a = [12,12,34,34,56,11,32,32,11,10,10]
g = len(a)
for i in range(g-1,0,-1):
if a[i] == a[i-1]:
del a[i]
print a
Can someone explain why, please?
You are deleting elements off the list, so it gets shorter.
You should create a new list:
last = None
b = []
for value in a:
if value != last:
b.append(value)
last = value

Categories

Resources