39

While looping over a list in Python, I was unable to modify the elements without a list comprehension. For reference:

li = ["spam", "eggs"]
for i in li:
    i = "foo"

li
["spam", "eggs"]

li = ["foo" for i in li]
li 
["foo", "foo"]

So, why can't I modify elements through a loop in Python? There's definitely something I'm missing, but I don't know what. I'm sure this is a duplicate, but I couldn't find a question about this, and if there is a link, that would be more than enough.

martineau
  • 119,623
  • 25
  • 170
  • 301
user2717129
  • 661
  • 3
  • 7
  • 11
  • 3
    See [this](http://stackoverflow.com/questions/4081217/how-to-modify-list-entries-during-for-loop), [this](http://stackoverflow.com/questions/1637807/modifying-list-while-iterating) – devnull Oct 10 '13 at 08:36
  • 1
    It is not a duplicate imo. The linked answers do not explain why the loop in the form `for item in list` does not modify items. – Jeyekomon Aug 16 '21 at 10:33

5 Answers5

51

Because the way for i in li works is something like this:

for idx in range(len(li)):
    i = li[idx]
    i = 'foo'

So if you assign anything to i, it won't affect li[idx].

The solution is either what you have proposed, or looping through the indices:

for idx in range(len(li)):
    li[idx] = 'foo'

or use enumerate:

for idx, item in enumerate(li):
    li[idx] = 'foo'
justhalf
  • 8,960
  • 3
  • 47
  • 74
  • Looping through the indices doesn't seem to be working for me, I'm using python 3.6. Never mind. I'm using a slice, that's why. – Valachio Jan 19 '18 at 00:14
14

In fact with list comprehension you are not modifying the list, you are creating a new list and then assigning it to the variable that contained the previous one.

Anyway, when you do for i in li you are getting a copy of each value of li in variable i, you don't get the reference to a position in li, so you are not modifying any value in li.

If you want to modify your list you can do it with enumerate:

>>> li = ["spam", "eggs"]
>>> for i,_ in enumerate(li):
        li[i] = "foo"
>>> li
 ['foo', 'foo']

or with xrange (in Python 2.7, use range in python 3):

>>> for i in xrange(len(li)):
        li[i] = "foo"
>>> li 
 ['foo', 'foo']

or with the list comprehension you showed in your question.

jabaldonedo
  • 25,822
  • 8
  • 77
  • 77
1

I'm able to modify a list while looping:

lst = range(10)  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

for i, elem in enumerate(lst):
    lst[i] = 0   // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
user278064
  • 9,982
  • 1
  • 33
  • 46
0

for element in li - returns you a copy of element, not the element itself.

Solution for your case would be:

for i in range(len(li)):
    li[i] = 'foo'
alandarev
  • 8,349
  • 2
  • 34
  • 43
0

Maybe using dictionaries might be helpful.

>>> li = {0: "spam", 1:"eggs"}
    for k, v in li.iteritems():
        li[k] = "foo"

>>> li
{0: 'foo', 1: 'foo'}
grasshopper
  • 421
  • 1
  • 3
  • 11