4
a =[1,2]
for entry in a:
    entry = entry + 1
print a  

Shouldn't the list be mutated to[2,3]? The result came out as [1,2]. Why?

Mark Skelton
  • 3,663
  • 4
  • 27
  • 47
NIO
  • 41
  • 1
  • 2
  • 6
    Possible duplicate of [How to add an integer to each element in a list?](http://stackoverflow.com/questions/9304408/how-to-add-an-integer-to-each-element-in-a-list) – albert Mar 08 '16 at 17:30
  • 2
    `for entry in a` creates a copy of each item in your list and presents it to you. If you modify it "in the loop", you're affecting the copy not the original list-object. Keep this in mind when using loops and things. – Torxed Mar 08 '16 at 17:32
  • 1
    @albert None of the answers in that question mutate the list, they return a new list. – Barmar Mar 08 '16 at 17:35
  • 1
    @Torxed: No, `for entry in a` does _not_ create a copy of each item; it binds the name `entry` to each item in `a` in turn. See my answer for a demo. – PM 2Ring Mar 08 '16 at 17:40
  • @albert et al: What Barmar said. The OP isn't exactly asking how to increment integers in a list, they're wondering why their code _doesn't_ modify the list items. – PM 2Ring Mar 08 '16 at 17:43
  • @PM2Ring In lay mans terms it is a copy because you can't directly work with the original object which `entry` in this case happens to be bound to. I'm terribly sorry for the miss-leading choice of words, I'm no academic and never try to pose as one. I get the logic and the jist of things, so I'm thankful there are others that can actually explain what i mean with academic and more in-depth detail of things. Also I do understand why my choice of words would be invalid, but I'll leave the comment for now as a back-trace to this discussion. – Torxed Mar 08 '16 at 17:45
  • 1
    @Torxed: Well, you can actually work with the original object via `entry`, if you're careful, as my code shows. And you can prove that it's not a copy by using the `id` function. – PM 2Ring Mar 08 '16 at 17:50
  • @PM2Ring Obviously, but not a list of integers (as you described). There for you can not do what OP asks us to do unless we either read between the lines and assume he/she means "mutated" and there for can create a copy of each element and create a new list based from it OR loop over a index and modify based on index instead. – Torxed Mar 08 '16 at 17:54
  • Also read [Scope of python variable in for loop](http://stackoverflow.com/q/15363138) – Bhargav Rao Mar 08 '16 at 18:04

4 Answers4

6

To modify the original list you could do this:

a =[1,2]
for x in range(len(a)):
    a[x] = a[x] + 1

print a

Or you could change your for loop to a "oneliner" like this:

a =[1,2]
a = [x + 1 for x in a]
print a

Output of either method:

[2, 3]

The difference between the methods is the first modifies the list, while the second creates a new list.

Mark Skelton
  • 3,663
  • 4
  • 27
  • 47
  • 3
    That doesn't mutate the list, it creates a new list and assigns it to the original variable. – Barmar Mar 08 '16 at 17:33
  • @Barmar I'm going out on a limb here and assume OP is used to another language or have heard/been told to mutate a list of integers and that's what the OP wants, but when in fact it's a technical term from other languages that is not applicable to Python as described by #PM2Ring, but if you read between the lines it is, if you "create a new list and assign it's original values to it in place of the variable". – Torxed Mar 08 '16 at 17:49
  • Note that you _can_ do a list comp and still preserve the original list object, if you _really_ want to by doing `a[:] = [x + 1 for x in a]` – PM 2Ring Mar 08 '16 at 18:11
5

No, because when you do entry = entry + 1 you create a new integer object and bind that to the name entry, the original object bound to that name is unaffected.

Remember that in Python integer objects are immutable. However, if a contains mutable objects, like lists, you can do this:

a = [[1], [2]]
for entry in a:
    entry += [1]
print a  

And then the items in a will be mutated:

output

[[1, 1], [2, 1]]

Note that if you just did entry = entry + [1] then a would be unchanged, due to the way simple assignment binds the new object to the name.

You may find this article helpful: Facts and myths about Python names and values, which was written by SO veteran Ned Batchelder.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • I will point out that the user probably wan'ts to keep the original structure of the list and not use list in lists as you pointed out to get it to work. Never the less, a good lesson in Python objects and why things work the way it does. – Torxed Mar 08 '16 at 17:47
  • 1
    @Torxed: Certainly, it's silly to use nested lists when you don't really need them. And the other answers have shown the sensible way to modify the list contents. The whole purpose of my example is to show that even though `entry` _does_ refer to the original list items the assignment process binds new objects to `entry`. Please take a look at Ned Batchelder's excellent article, I think you'd find it illuminating. – PM 2Ring Mar 08 '16 at 17:54
  • Clear and good point, hence it's well deserved of an up-vote on my behalf. And I wish to keep this as friendly discussion since I'm (again) not a academic and have a history of stiring up trouble with logical analogues rather than correct terminology. There for I'll give it an up-vote and wish you to keep up the good work! – Torxed Mar 08 '16 at 17:57
3

All variables in Python contain references (i.e., pointers) to some object stored somewhere. Even integers are objects. Assignment changes the pointer to point to another object, it does not modify the item that is being pointed to.

When you do:

a = [1, 2]
for entry in a:
    entry = entry + 1

The first time through the loop, entry is made to point to the integer 1, because that's what the first element of a (known as a[0]) points to.

Now, the integer 1 is not stored in the list itself, and entry is not a pointer to a slot in the list. Rather, entry and a[0] point to the same object, the integer 1.

When you do entry = entry + 1 (or just entry += 1), entry is changed to point to the integer 2. However, a[0] does not change, because you didn't change it. It still points to the integer 1.

The Pythonic way to modify a list while iterating over it is to use enumerate(). This gives you both the index (which you need to modify a list item) and the value from the list.

for index, entry in enumerate(a):
    a[index] += 1

Here you are not actually using the existing element value, so you could also use range:

for index in range(len(a)):
    a[index] += 1

Another way to do this is with a slice assignment coupled with a generator expression. This replaces the entire contents of the list:

 a[:] = (entry + 1 for entry in a)

Doing it this way has the advantage that, if the list a also has other names, the changed list is visible through those other names. If this isn't what you want, you can also do:

 a = [entry + 1 for entry in a]

This creates a new list with the updated values and makes a point to it.

kindall
  • 178,883
  • 35
  • 278
  • 309
1

No, why should it?

entry is just a name, which the for loop assigns to each integer in the list. If you subsequently reassign that name to point to a different integer, the list doesn't care.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895