2

I am coming to ask you another basic question again. :) I want to change a list via for-loop.(sorry to bother, but i am new....)

Here is the first script:

L=[1,2,3]
i=0
for a in L:
    L[i]+=1
    i+=1
print(L)
print(a)

I got:

L:[2,3,4]
a: 3

This is exactly what i want. But i notice that if i change the script to this:

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

And i got:

L:[1,2,3]
i:4

I can understand that i must be 4. But the question is why the list L doesn't change. In my opinion, the objects in the list are given to the variable i. So variable i is the one who can edit the object. So basically, the list should change. Actually, however, it stays the same.

Can someone explain this?

Thank you!

Harry
  • 77
  • 8
  • 1
    `i` is just a looping variable. Why do you think changing `i` should change value in list? – Austin Jun 22 '18 at 11:04
  • According to your code snippet, the list "L" is not suppose to change as "i" is just a counter veriable and holds single element from "L" every time it iterate. – Sonam Mohite Jun 22 '18 at 11:07
  • @Austin,@Sonam Mohite, because i think the element in the list (the objects) was given to the variable i. This is the same as i = L[0], i =2. Then L[0]=2 – Harry Jun 22 '18 at 11:10
  • @Harry You can find *why value is not changed* in the above link. – Austin Jun 22 '18 at 11:13
  • ok! Thank you! I will have a look – Harry Jun 22 '18 at 11:16

5 Answers5

5

This has to do with what Python "variables" really are: name pointings to objects. Think of it as a dictionary with names as keys and objects as values.

In your for loop, i initially points to the current iteration item in the list, ie i points to L[0], then to L[1] etc. But when you do i += 1 - which actually translates to i = i+1 - you rebind i so that it points to another object. This only affects the name i (what value the key holds), not the object it was pointing to before, so your list is indeed not changed.

For a more in-depth explanation, you want to read Ned Batchelder's excellent article. This will save you a lot of time and frustrations ;)

NB : for your example snippet, the proper way to update your list in place is to use enumerate:

for index, value in enumerate(L):
    L[index] = value + 1

or to update the whole list inplace with a list comprehension:

L[:] = [value+1 for value in L]
DavidG
  • 24,279
  • 14
  • 89
  • 82
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • yes! What a perfect explanation! Thank you! – Harry Jun 22 '18 at 11:41
  • 1
    @Harry I second Bruno's recommendation to read Ned's article. In fact, when I read your question I almost posted a link to that article in a comment, until I saw that it was already linked in this answer. There's a shorter article with nice diagrams on this important topic at [Other languages have "variables", Python has "names"](http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables). – PM 2Ring Jun 22 '18 at 11:54
  • @PM2Ring, thank you! i will have a look! thank you! – Harry Jun 22 '18 at 11:55
1

But the question is why the list L doesn't change

Because if you assign anything to i, it won't affect L[i].

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

for index, item in enumerate(L):

Another approach is to use a list comprehension

L[:] = [i + 1 for i in L]
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
  • I see, but i think the list are mutable. I can change the value of the original list instead of make some change and copying it to a new one? – Harry Jun 22 '18 at 11:08
  • `L = [i + 1 for i in L]` will NOT change the list in place, so it doesn't do what the OP asks for. It has to be `L[:] = [i + 1 for i in L]` instead. – bruno desthuilliers Jun 22 '18 at 11:32
1

Thanks for all your answers! But this link is really comprehensive. https://nedbatchelder.com/text/names.html This is the same as:

m=[1,2,3]
i=m[0]  #here i refers to m[0], namely 1
i=4     #here i refers to a new value

The result is obviously:

m=[1,2,3]
i=4

For the reason that the int-name can't be changed originally. Once the value of i changed, it refers to another value (object). So the List won't change.

Thank you all the same.

Harry
  • 77
  • 8
-1

When you do your loop

for i in L :
    i+=1

You never ask Python to change your list L. In fact, you just update i. At first, i = L[0] = 1, You update i to 2. But L doesn't change because you didn't ask L[0] to be updated. As L includes numbers, when you do for i in L, then i is not the element of L itself, but a clone of it.

A proper way to do it would be :

for i in range(len(L)) :
    L[i] += 1 #you update L[i] itself this time

or if you like a more compact way :

L = [i+1 for i in L]

I hope I was clear enough. Have a nice day ;-)

Kwikerr
  • 45
  • 1
  • 6
  • 3
    "when you do for i in L, then i is not the element of L itself, but a clone of it." => wrong. Well, mostly wrong... What `i` points to IS the list's element, but _rebinding_ the _name_ `i` doesn't change the list. See https://nedbatchelder.com/text/names.html for a more in-depth explanation. – bruno desthuilliers Jun 22 '18 at 11:11
  • @brunodesthuilliers, thx! i agree with you – Harry Jun 22 '18 at 11:22
  • 1
    Oh BTW, your last "proper way" doesn't do the same thing as what the OP asks for - it rebinds `L` but does NOT change the list `L` was pointing to, so other references to this original list will remain unchanged. You want `L[:] = [i+1 for i in L]` to have the same behaviour (update the list in place). – bruno desthuilliers Jun 22 '18 at 11:30
  • @brunodesthuilliers Yet, if `L = [[1],[2],[3]]` and you do the loop with `i += [1]` then L is updated correctly. So when i is a list, it is clearly the element. But in this example, it's a number and you can change it independently in L or in i. Don't want to show my stupidity entirely, but doesn't it mean that i and the element of L are two different variables ? Which in that case, would imply that i is a clone of the element. Sorry if the question is dumb :) – Kwikerr Jun 22 '18 at 12:10
  • 1
    @Kwikerr You really need to study Ned's article. Also see http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables – PM 2Ring Jun 22 '18 at 12:14
  • 1
    @Kwikerr in this case the augmented assignment operator is overriden to also execute a `list.extend()` operation and return the LHS list - so you actually have both a rebind operation (that by itself doesn't change `L`) AND a mutation of the list `i` is pointing to. Replace `i += [1]` with `i.extend([1])` and you'll have the real behaviour. If you really want to understand python's "variables" you have to totally forget the traditional C/Pascal/etc definition of "variable" (where a variable is a memory address) and think in terms of "names" and "bindings". – bruno desthuilliers Jun 22 '18 at 12:19
-1
for i in L:

This means while iterating over the list, value of each element in list is stored in i.

So if you change the value of i no change will happen in the list

Consider this-

a=2
b=a
b=3

Changing b does not change the value of a.

  • Thank you! I understand this, but this is for integer variable. Integer can't be changed originally. But List can get changed. – Harry Jun 22 '18 at 11:19
  • Can you explain what do you mean by Integer can't be changed originally? – Keshav Bansal Jun 22 '18 at 11:25
  • 2
    @KeshavBansal python integers are immutable, lists are mutable. And stating that "element in list is stored in i" is misleading at best and actually plain wrong from a technical POV - "i" is just a name, not a memory address, so nothing is "stored in" it. – bruno desthuilliers Jun 22 '18 at 11:35