0

You are not supposed to modify a list during iteration apparently. I can see the potential for problems, but I'm not sure when it is or isn't OK. For example, the code below seems to work fine:

import random

xs = [random.randint(-9,9) for _ in range(5)]
print(xs, sum(xs))
xs = [x * -1 for x in xs]
print(xs, sum(xs))

Is this OK because the list comprehension works differently to a normal for loop?

This version seems equally unproblematic:

import random

xs = [random.randint(-9,9) for _ in range(5)]
print(xs, sum(xs))

for i in range(len(xs)):
    xs[i] *= -1
    
print(xs, sum(xs))

So it looks like for this kind of use case there is no issue, unless I'm missing something.

What then are the situations when it is or isn't OK to modify a list while iterating over it?

Robin Andrews
  • 3,514
  • 11
  • 43
  • 111
  • _You are not supposed to modify a list during iteration_ You are not supposed to _add_ or _remove_ items (thus altering the **length** of the list) during iteration. Replacing individual elements of the list is okay, because this does not alter the **length**. – John Gordon Apr 08 '23 at 20:47
  • In the first case you are not modifying the original list in any way (but instead creating a completely separate, new one), and in the second case you are not iterating over it (but instead over a `range` object). There are two separate questions here, both of which are not entirely clear because they are based on misconceptions. That said, you still are allowed to modify the individual elements, as that doesn't interfere with iteration - see the linked duplicate. – Karl Knechtel Apr 08 '23 at 20:55
  • Why do you say the second example is not iterating over the list? – Robin Andrews Apr 08 '23 at 20:57
  • Because `xs` is your list, therefore iterating over it would look like `for i in xs:`. By writing `for i in range(len(xs)):`, you create `range(len(xs))` (an object that represents a sequence of integers ranging from 0 up to the length of `xs` minus one), and then iterate over that. Incidentally: if that sounds complex, that's because it's not how you're intended to do things (even if it works and is assured to work). See also e.g. https://stackoverflow.com/questions/18294534, https://stackoverflow.com/questions/4383250. – Karl Knechtel Apr 08 '23 at 20:59
  • But you are sequentially accessing the indices generated by the range, which seems to me to be functionally equivalent to iterating over the list. At a lower level, I expect the `for i in xs:` version is implemented more like the `range` version. Do you disagree? – Robin Andrews Apr 08 '23 at 21:02
  • I disagree because *iteration* is a fundamentally high-level concept which a) has a specific meaning that has nothing to do with data access patterns within the loop and b) stands apart from implementation. If I wrote a `for` loop over a list and used the elements of the list to access a separate dictionary, for example, that would clearly not be "iterating over the dictionary" even if the list elements happened to map one-to-one onto the dict keys. – Karl Knechtel Apr 08 '23 at 21:36
  • I both agree and disagree. I can see that the object containing the indices could in principle contain random values within the required range, for example, but iterating "by proxy" of an ordered sequence containing all the indices still seems a lot like iteration to me. Maybe "indirect" or "secondary" iteration. You've got me thinking through. – Robin Andrews Apr 08 '23 at 21:52

2 Answers2

0

List comprehensions are always fine because you are creating a new list from the one you are iterating over, not modifying the one you are iterating over.

It is also ok to modify a list in a for loop, as long as you don't add/remove any elements.

The problem comes when you add elements to / remove elements from a list. This causes the length of the list to change, so the for loop doesn't know when to end.

The for loop will always try to run to the original length of the list, which will cause an index error if you have shortened the list.

If you have made the list longer, then the for loop will not go to all of the elements in the list.

Lecdi
  • 2,189
  • 2
  • 6
  • 20
0

Run the below code and you will see the difference. in you case you are precomputing the len probably thus change in the list is immeterial

import random

xs = [random.randint(-9,9) for _ in range(5)]
print(xs, sum(xs))

[-7, 9, 2, 4, -9] -1


print(xs)

N = len(xs)

for i, val in enumerate(xs):
    print(val)
    xs[N-i-1] *= -1
    
print(xs, sum(xs))


[7, -9, -2, -4, 9] 1
mks2192
  • 306
  • 2
  • 11