Search code examples
pythonnested-listsupdating

In place/directly updating list of lists inside iteration - Should this be working?


I accidently noticed that directly updating elements in a list of lists was working.

There are lots of Q&A about updating a list of lists. Yet all the answers I inspected indicate that

  • inside the iteration python generates a copy of the list's elements
  • changing the copied element does not change the list itself
  • you need to use a list comprehension, an index into the list or something alike to change the list

But in the following example I can directly update a list.

All answers I found about the subject are fairly old, so is this later on added to python?

# COMPREHENSION ==================================
li = [
    ["spam1", "eggs1"],
    ["spam2", "eggs2"]
    ]

print("\nCOMPREHENSION ---------------")
print("INPUT", li)
li = [["new", l[1]] for l in li]
print("OUTPUT", li)

# INDEX ===========================================
li = [
    ["spam1", "eggs1"],
    ["spam2", "eggs2"]
    ]
print("\nINDEX -----------------")
print("INPUT", li)
for index, l in enumerate(li):
    li[index] = ["new", l[1]]
print("OUTPUT", li)

# IN PLACE ========================================
li = [
    ["spam1", "eggs1"],
    ["spam2", "eggs2"]
    ]
print("\nIN PLACE --------------")
print("INPUT", li, "       <<<<<<<<<<<<<")
for i in li:
    i[0] = f'new'
print("OUTPUT", li, "      <<<<<<<<<<<<<")

Result

COMPREHENSION ---------------
INPUT [['spam1', 'eggs1'], ['spam2', 'eggs2']]
OUTPUT [['new', 'eggs1'], ['new', 'eggs2']]

INDEX -----------------
INPUT [['spam1', 'eggs1'], ['spam2', 'eggs2']]
OUTPUT [['new', 'eggs1'], ['new', 'eggs2']]

IN PLACE --------------
INPUT [['spam1', 'eggs1'], ['spam2', 'eggs2']]        <<<<<<<<<<<<<
OUTPUT [['new', 'eggs1'], ['new', 'eggs2']]           <<<<<<<<<<<<<

Solution

  • Everything actually works as expected. You are right, that inside iteration element is just copied, so changing the copied element does not change actual list. See example:

    In [1]: x = [1,2,3,4,5,6,7,8]
    
    In [2]: for a in x:
      ...:     if a ==2:
      ...:         a=3
    
    In [3]: x
    Out[3]: [1, 2, 3, 4, 5, 6, 7, 8]
    

    What happens, when an element of list is actually a list?

    In [4]: z = [[1,2],[3,4],[5,6]]
    
    In [5]: for a in z:
      ...:     if a[0] ==3:
      ...:         a = [7,8]
    
    In [6]: z
    Out[6]: [[1, 2], [3, 4], [5, 6]]
    

    You have a copy of an inner list assigned to a, so assigning to a another list does not change outer list z.

    However what is actually copied inside iteration is not an inner list, but a reference to it. So a points to inner list itself, not its copy. You can actually modify this inner list elements. Like here:

    In [8]: for a in z:
      ...:     if a[0] ==3:
      ...:         a[0] = 7
    
    In [9]: z
    Out[9]: [[1, 2], [7, 4], [5, 6]]
    

    Or even add/remove elements from it:

    In [10]: for a in z:
       ...:     if a[0] ==1:
       ...:         a.append(22)
    
    
    In [10]: z
    Out[10]: [[1, 2, 22], [7, 4], [5, 6]]