7

When I try this code:

bar = [1,2,3]
print(bar)
for foo in bar:
    print(id(foo))
    foo = 0
    print(id(foo))
print(bar)

I get this result:

[1, 2, 3]
5169664
5169676
5169652
5169676
5169640
5169676
[1, 2, 3]

I expected the end result to be [0,0,0] and that id would return identical values for each iteration. Why does it behave like this? How can I elegantly assign back to the elements of the list, without using enumerate or range(len(bar))?


See also: How to change variables fed into a for loop in list form

0

4 Answers 4

28

First of all, you cannot reassign a loop variable—well, you can, but that won’t change the list you are iterating over. So setting foo = 0 will not change the list, but only the local variable foo (which happens to contain the value for the iteration at the begin of each iteration).

Next thing, small numbers, like 0 and 1 are internally kept in a pool of small integer objects (This is a CPython implementation detail, doesn’t have to be the case!) That’s why the ID is the same for foo after you assign 0 to it. The id is basically the id of that integer object 0 in the pool.

If you want to change your list while iterating over it, you will unfortunately have to access the elements by index. So if you want to keep the output the same, but have [0, 0, 0] at the end, you will have to iterate over the indexes:

for i in range(len(bar)):
    print id(bar[i])
    bar[i] = 0
    print id(bar[i])
print bar

Otherwise, it’s not really possible, because as soon as you store a list’s element in a variable, you have a separate reference to it that is unlinked to the one stored in the list. And as most of those objects are immutable and you create a new object when assigning a new value to a variable, you won’t get the list’s reference to update.

Sign up to request clarification or add additional context in comments.

5 Comments

+1 for the discussion of CPython's implementation details. This means that, if you could change the value stored at 5169664, doing so would be disastrous because it would change every variable that is currently set to 1.
enumerate might be better than the solution with len(bar).
I think it's the correct answer at the part of the question:Why it happen? for What is the pythonic way to do it? see @Kevin answer
The documentation for ID says, "Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value." Reading this, I'm surprised that id(foo) ever changes.
For others who might encounter this question: Note that although if the object in the list you're referring to is mutable, then you may actually update it through this variable i, e.g. through += (__iadd__). See stackoverflow.com/questions/41446833/…
4

Yes, the output you got is the ordinary Python behavior. Assigning a new value to foo will change foo's id, and not change the values stored in bar.

If you just want a list of zeroes, you can do:

bar = [0] * len(bar)

If you want to do some more complicated logic, where the new assignment depends on the old value, you can use a list comprehension:

bar = [x * 2 for x in bar]

Or you can use map:

def double(x):
    return x * 2

bar = map(double, bar)

1 Comment

I think it's the correct answer at the part of the question: What is the pythonic way to do it? for Why it happen? see @Poke answer
0

you actually didnt change the list at all. the first thing for loop did was to assign bar[0] to foo(equivalent to foo = bar[0]). foo is just an reference to 1. Then you assign another onject 0 to foo. This changed the reference of foo to 0. But you didnt change bar[0]. Remember, foo as a variable, references bar[0], but assign another value/object to foo doesn't affect bar[0] at all.

Comments

0
bar = [0 for x in bar]

Long answer : foo is just a local name, rebinding does not impact the list. Python variables are really just key:value pairs, not symbolic names for memory locations.

1 Comment

Since your for will be returning a list, you don't technically need the [:]

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.