3

I want to re-assign each item in a list in Python.

In [20]: l = [1,2,3,4,5]
In [21]: for i in l:
   ....:     i = i + 1
   ....:     
   ....:     

But the list didn't change at all.

In [22]: l
Out[22]: [1, 2, 3, 4, 5]

I want to know why this happened. Could any body explain the list iterating in detail? Thanks.

zfz
  • 1,597
  • 1
  • 22
  • 45

4 Answers4

7

You can't do it like that, you are merely changing the value binded to the name i. On each iteration of the for loop, i is binded to a value in the list. It is not a pointer in the sense that by changing the value of i you are changing a value in the list. Instead, as I said before, it is simply a name and you are just changing the value that name refers to. In this case, i = i + 1, binds i to the value i + 1. So you aren't actually affecting the list itself, to do that you have to set it by index.

>>> L = [1,2,3,4,5]
>>> for i in range(len(L)):
        L[i] = L[i] + 1

>>> L
[2, 3, 4, 5, 6]

Some pythonistas may prefer to iterate like this:

for i, n in enumerate(L): # where i is the index, n is each number
    L[i] = n + 1

However you can easily achieve the same result with a list comprehension:

>>> L = [1,2,3,4,5]
>>> L = [n + 1 for n in L]
>>> L
[2, 3, 4, 5, 6]

For more info: http://www.effbot.org/zone/python-objects.htm

jamylak
  • 128,818
  • 30
  • 231
  • 230
  • Why do `id(l[0])` and `for i in l: print id(i)` print out the same ID? – Blender Apr 16 '13 at 07:16
  • @Blender because they both reference the same object. Was there something wrong with my explanation? – jamylak Apr 16 '13 at 07:17
  • Use `enumerate(L)` and not `range(len(L))`. A list comprehension will only what is intended if you re-assign it to the same name: `L = [i+1 for i in L]` otherwise the expression doesn't change the original `L`. – Burhan Khalid Apr 16 '13 at 07:18
  • @BurhanKhalid good idea, except I left `range` there, it's not taboo or anything – jamylak Apr 16 '13 at 07:29
1

This is because of how Python handles variables and the values they reference.

You should modify the list element itself:

for i in xrange(len(l)):
  l[i] += 1
unwind
  • 391,730
  • 64
  • 469
  • 606
1
>>> a = [1, 2, 3, 4, 5]
>>> a = [i + 1 for i in a]
>>> a
[2, 3, 4, 5, 6]
Yarkee
  • 9,086
  • 5
  • 28
  • 29
0

Initially i is a pointer to the item inside the list, but when you reassign it, it will point to the new number, that is why the list will not be changed.

For a list of mutable objects it would work:

class Number(object):
    def __init__(self,n):
        self.n=n
    def increment(self):
        self.n+=1
    def __repr__(self):
        return 'Number(%d)' % self.n

a = [Number(i) for i in xrange(5)]

print a
for i in a:
    i.increment()
print a

But int are not mutable, when you do an operation on them you get a new int object, and that is why it doesn't work in your case.

LtWorf
  • 7,286
  • 6
  • 31
  • 45