In-place modification of Python lists

Question:

I am trying to perform in-place modification of a list of list on the level of the primary list. However, when I try to modify the iterating variable (row in the example below), it appears to create a new pointer to it rather than modifying it.


Smallest example of my problem.

c = [1,2,3]
for x in c:
    x = x + 3
print(c) #returns [1,2,3], expected [4,5,6]

The above example is a trivial example of my problem. Is there a way to modify x elementwise, in-place and have the changes appear in C?


Less trivial example of my problem. I am switching all 0’s to 1’s and vice-versa.

A =  [[1,1,0],
      [1,0,1],
      [0,0,0]]
for row in A:
    row = list(map(lambda val: 1 - val, row))
print(A)

Expected

A = [[0,0,1],
     [0,1,0],
     [1,1,1]]

Returned

A = [[1,1,0],
     [1,0,1],
     [0,0,0]]

update:
Great answers so far. I am interested how the iterating variable (row in the second example) is linked to the iterable variable (A in the second example).

If I do the following, which reverses each sublist of A, it works perfectly.

Why does the following example, where I modify the iterating variable works but the above examples do not?

A =  [[1,1,0],
  [1,0,1],
  [0,0,0]]

for row in A:
    row.reverse()
print(A)

#returns, as expected
A = [[0, 1, 1],
     [1, 0, 1], 
     [0, 0, 0]]
Asked By: Alexander

||

Answers:

I found this in the docs: https://docs.python.org/3/tutorial/controlflow.html#for

Python’s for statement iterates over the items of any sequence (a list
or a string), in the order that they appear in the sequence.

If you need to modify the sequence you are iterating over while inside
the loop (for example to duplicate selected items), it is recommended
that you first make a copy. Iterating over a sequence does not
implicitly make a copy.

I was wrong in my first response, when iterating through a list it returns the actual items in that list. However, it seems they cannot be edited directly while they are being iterated through. This is why iterating through the integers the length of the list works.

As for why the .reverse() function works, I think it’s because it is affecting a list instead of a value. I tried to use similar built in functions on nonlist datatypes like .replace() on strings and it had no effect.

All of the other list functions I tried worked: .append(), .remove(), and .reverse() as you showed. I’m not sure why this is, but I hope it clears up what you can do in for loops a bit more.

Answer to old question below:

The way you are using the for loops doesn’t affect the actual list, just the temporary variable that is iterating through the list. There are a few ways you can fix this. Instead of iterating through each element you can can count up to the length of the list and modify the list directly.

c = [1,2,3]
for n in range(len(c)):
    c[n] += 3
print(c) 

You can also use the enumerate() function to iterate through both a counter and list items.

c = [1,2,3]
for n, x in enumerate(c):
    c[n] = x + 3
print(c) 

In this case, n is a counter and x is the item in the list.

Finally, you can use list comprehension to generate a new list with desired differences in one line.

c = [1, 2, 3]
d = [x + 3 for x in c]
print(d)
Answered By: Aeolus

The usual way to poke values into an existing list in Python is to use enumerate which lets you iterate over both the indices and the values at once — then use the indices to manipulate the list:

c = [1,2,3]
for index, value in enumerate(c):
    c[index] = value + 3

For your second example you’d do almost the same:

A =  [[1,1,0],
  [1,0,1],
  [0,0,0]]

for row in A:
    for index, val in row:
        row[index] = 0 if val > 0 else 1

In the second example the list objects in A become the loop variable row — and since you’re only mutating them (not assigning to them) you don’t need enumerate and the index

Answered By: theodox

If you want to keep it consice without creating an additional variable, you could also do:

c = [1,2,3] 
print(id(c)) 
c[:] = [i+3 for i in c] 
print(c, id(c))

Output:

2881750110600
[4, 5, 6] 2881750110600
Answered By: jaya chithra

Using list comprehension here also will work:

A = [[1,1,0],
     [1,0,1],
     [0,0,0]]

A = [[0 if x > 0 else 1 for x in row] for row in A]

print(A)

Output:

[[0, 0, 1],
 [0, 1, 0],
 [1, 1, 1]]
Answered By: delloff
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.