42

When using a for loop in Python to iterate over items in a list, will changing item (below) change the corresponding item in items?

for item in items:
    item += 1

Will each item in items be incremented or remain the same as before the loop?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
James
  • 30,496
  • 19
  • 86
  • 113
  • 11
    Why don't you just try it? – David Zwicker Feb 11 '13 at 15:04
  • Nope; does this [previous answer of mine](http://stackoverflow.com/questions/12080552/python-list-doesnt-reflect-variable-change-new-to-python/12080644#12080644) help at all? – Martijn Pieters Feb 11 '13 at 15:05
  • I don't think it will chage the value (couldn't you try it?) – Paul Gleeson Feb 11 '13 at 15:05
  • 3
    @MartijnPieters -- Doesn't this depend critically on what type of object `item` actually is? What if `items` are class instances with an appropriately defined `__iadd__`? – mgilson Feb 11 '13 at 15:06
  • 1
    @mgilson: Ah, the question was edited. It was an assignment of `item = item + 1` before. – Martijn Pieters Feb 11 '13 at 15:07
  • @DavidZwicker Because it may help others who don't have both Python (or either) installed. Also, if it was that easy why not answer and get some rep? :P – James Feb 11 '13 at 15:10
  • 3
    Martijn Pieters already answered your question, but peripherally related, this is the Pythonic way of doing it: `items = [x + 1 for x in items]` – sampson-chen Feb 11 '13 at 15:21

2 Answers2

37

No, variables in Python are not pointers.

They refer to objects on a heap instead, and assigning to a variable doesn't change the referenced object, but the variable. Variables and objects are like labels tied to balloons; assignment reties the label to a different balloon instead.

See this previous answer of mine to explore that idea of balloons and labels a bit more.

That said, some object types implement specific in-place addition behaviour. If the object is mutable (the balloon itself can change), then an in-place add could be interpreted as a mutation instead of an assignment.

So, for integers, item += 1 is really the same as item = item + 1 because integers are immutable. You have to create a new integer object and tie the item label to that new object.

Lists on the other hand, are mutable and lst += [other, items] is implemented as a lst.__iadd__([other, items]) and that changes the lst balloon itself. An assignment still takes place, but it is a reassigment of the same object, as the .__iadd__() method simply returns self instead of a new object. We end up re-tying the label to the same balloon.

The loop simply gives you a reference to the next item in the list on each iteration. It does not let you change the original list itself (that's just another set of balloon labels); instead it gives you a new label to each of the items contained.

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    Most specifically, isn't it a mutation followed by an assignment? You mutate the object in a function and then you make an assignment to whatever that function returns (which *should* be just the object itself, but doesn't have to be)? -- In other words, `i += something` is the same thing as `i = i.__iadd__(something)` – mgilson Feb 11 '13 at 15:11
  • 1
    @mgilson: yes, `.__iadd__()` is expected to return `self` in that case. – Martijn Pieters Feb 11 '13 at 15:13
  • Yes, expected to, but it doesn't have to. I find it instructive to point that out as it explains very clearly how you can get different behavior using `+=` with mutable and immutable objects. – mgilson Feb 11 '13 at 15:16
13

Well, it really depends on the items.

Take the following case:

class test():
    pass

a = test()
a.value = 1

b = test()
b.value = 2

l = [a,b]

for item in l:
    item.value += 1

for item in l:
    print item.value

>>> 
2
3

and in this case:

l2 = [1,2,3]

for item in l2:
    item += 1

for item in l2:
    print item

>>> 
1
2
3

So as you can see, you need to understand the pointers as Martijn said.

Community
  • 1
  • 1
Inbar Rose
  • 41,843
  • 24
  • 85
  • 131
  • 1
    You're right, it technically depends on the items (namely on the definition of `__iadd__`). But not in the way you show: Item assignment *always* works like that. What you show is that attribute assignment is different from local assignment, but that's an entirely different matter (though related and also important). -1 –  Feb 11 '13 at 15:08
  • 2
    This is not correct; you are not assigning to a local variable, you are assigning to an attribute on another object. That is mutating the mutable object, not a local variable. – Martijn Pieters Feb 11 '13 at 15:10
  • 1
    I never claimed otherwise, just showing that when iterating over a list in python you can do work on the items in certain situations, in other languages it is not allowed to manipulate the object/list you are iterating. – Inbar Rose Feb 11 '13 at 15:11
  • 1
    Okay, but that doesn't make your answer more relevant to the question. (Also note that you *aren't* mutating the list either way, you're just mutating objects which are referenced from several places, one of which happens to be a slot in a list.) –  Feb 11 '13 at 15:16
  • 1
    The OPs question is vague on the purpose or his desire. I simply know that the OP wants to iterate over a list, and is asking about 'in python' leading me to understand 'as opposed to other languages' so I provided two samples, one where iterating and changing works, and one where it doesn't. I hope it can guide the OP, or at least attempt to inform him in some way. – Inbar Rose Feb 11 '13 at 15:19