7

I have the following code, why the first one doesn't change alist while the second changes it?

alist = [[1,2], [3,4], [5,6]]
for item in alist:
    item = 1
print(alist)

alist = [[1,2], [3,4], [5,6]]
for item in alist:
    item = item.append(10)
print(alist)
nalzok
  • 14,965
  • 21
  • 72
  • 139
Daniel Wu
  • 5,853
  • 12
  • 42
  • 93
  • First change if used like this : `for item in alist : alist[alist.index(item)]= 1` So `item` is local variable in a loop, do nothing outside. – dsgdfg Oct 09 '16 at 09:19

7 Answers7

6

This is a somewhat unintuitive behavor of variables. It happens because, in Python, variables are always references to values.

Boxes and tags

In some languages, we tend to think about variables as "boxes" where we put values; in Python, however, they are references, and behave more like tags or "nicknames" to values. So, when you attribute 1 to item, you are changing only the variable reference, not the list it is pointing to.

A graphic representation can help. The image below represents the list created by alist = [[1,2], [3,4], [5,6]]

A list with three sublists

Given that, let's see what happens when we execute your first loop.

The first loop

When you execute for item in alist, you are asking the interpreter to take each value from the list, one per time, put it on the variable item and do some operation in it. In the first operation, for example, we have this new schema:

Now a variable points to a sublist

Note that we do not copy the sublist to item; instead, we point to it through item. Then, we execute item = 1 — but what does it mean? It mean that we are making item point to the value 1, instead of pointing to the sublist:

item points to other value now

Note that the old reference is lost (it is the red arrow) and now we have a new. But we just changed a variable pointing to a list — we did not alter the list itself.

Then, we enter to the second iteration of the loop, and now item points to the second sublist:

item pointing to the second sublist

When we execute item = 1, again, we just make the variable point to aonther value, without changing the list:

enter image description here

Now, what happens when we execute the second loop?

The second loop

The second loop starts as the first one: we make item refer to the first sublist:

Now a variable points to a sublist

The first difference, however, is that we call item.append(). append() is a method, so it can change the value of the object it is calling. As we use to say, we are sending a message to the object pointed by item to append the value 10. In this case, the operation is not being made in the variable item, but directly in the object it refers! So here is the result of calling item.append():

A list has grown

However, we do not only append a value to the list! We also assign the value returned by item.append(). This will sever the item reference to the sublist, but here is a catch: append() returns None.

enter image description here

The None value

None is a value that represents, basically, the unavailability of a relevant value. When a function returns None, it is saying, most of the time, "I have nothing relevant to give you back." append() does change its list directly, so there is nothing it needs to return.

It is important because you probably believed item would point to the appended list [1, 2, 10], right? No, now it points to None. So, you would expect the code below...

alist = [[1,2], [3,4], [5,6]]
for item in alist:
    item = item.append(10)
    print(item)

To print something like this:

[1, 2, 10]
[3, 4, 10]
[5, 6, 10]

But this does not happen. This is what happens:

>>> alist = [[1,2], [3,4], [5,6]]
>>> for item in alist:
...     item = item.append(10)
...     print(item)
...
None
None
None

Yet, as we commented, the append() method changed the lists themselves. So, while the item variable was useless after the assignment, the final list is modified!

>>> print alist
[[1, 2, 10], [3, 4, 10], [5, 6, 10]]

If you want to use the appended list inside the loop, just do not assign the method returned value to item. Do this:

>>> alist = [[1,2], [3,4], [5,6]]
>>> for item in alist:
...     item.append(10)
...     print item
... 
[1, 2, 10]
[3, 4, 10]
[5, 6, 10]

This works because item will still point to the list.

Conclusion

References are somewhat complex to understand at first, let alone master. Yet, they are really powerful and can be learned if you follow examples etc. Your example is a bit complicated because there is more happening here.

The Python Tutor can help you understand what is going on, because it executes each step graphically. Check your own code running there!

brandizzi
  • 26,083
  • 8
  • 103
  • 158
4

In the first example item is bound to each element in list alist, and then item is rebound to the integer 1. This does not change the element of the list - it merely rebinds the name item to the int object 1.

In the second example the list element (itself a list) is mutated by append(). item is still bound to the sub-list so item.append() mutates the sub-list.

mhawke
  • 84,695
  • 9
  • 117
  • 138
2

Are you forgetting that list.append() does not return the list itself but actually modifies the list in place?

item = 1 does as expected. For the rest of the for-loop, item is now 1, and not the list it originally was. It won't reassign what item is, that's not what for-loops do.

However, in your second loop, you're now assigning item = None, because the append function does not return anything but it appends the item to the list in place:

>>> L = [1, 2, 3]
>>> L.append(4)
>>> L
[1, 2, 3, 4]

Thus, your code is basically saying "go through each sublist in my main list and append 10 to it".

TerryA
  • 58,805
  • 11
  • 114
  • 143
2

The = operator does not make any change in the second code, using .append causes changes in alist. Use following line as the third line in the second code. You will see the same result:

item.append(10)

In the first code item point to another object by item=1, so alist does not change. In the second code you make change on alist by calling append method of it.

SuB
  • 2,250
  • 3
  • 22
  • 37
2

It's pointer variable in python.

The first example:

for item in alist:
   item = 1

each item is pointing to each _item in alist but suddenly you change the item value, not the of the value of the _item in alist, as a result nothing change to alist

The second example:

for item in alist:
    item = item.append(10)

each item is pointing to each _item in alist and then you append something to the item sharing the same memory location of _item, as a result value of _item in alist are changed and alist is changed too.

1

Referencing the document:

The for-loop makes assignments to the variables(s) in the target list. This overwrites all previous assignments to those variables including those made in the suite of the for-loop:

for i in range(10):
    print(i)
    i = 5             # this will not affect the for-loop
                      # because i will be overwritten with the next
                      # index in the range

So your second example is identical to:

alist = [[1,2], [3,4], [5,6]]
for item in alist:
    item.append(10)    # this statement modifies each item in alist
                       # , which is what item the variable "points to"
print(alist)

That is to say, item is a new variable in every iteration. As a result, assigning values to it in any specific iteration is pointless, because its value will be overridden by the reference to the next item in alist at the very beginning of the next iteration.

nalzok
  • 14,965
  • 21
  • 72
  • 139
0

When you write

for item in alist:

You are actually creating a copy of each item in liast in the item variable, and you are not getting a reference to item.

However, append changes the list in-place and doesn't return a value, and that's why you're getting the values changed to None (due to the assignment - if you remove it you'll get the appending working fine).

Maroun
  • 94,125
  • 30
  • 188
  • 241
  • `for each` block in python does not make any copy, i.e `item` points to each member of `alist` in every iteration. – SuB Oct 09 '16 at 08:19