Let it
be an iterable element in python.
In what cases is a change of it
inside a loop over it
reflected? Or more straightforward: When does something like this work?
it = range(6)
for i in it:it.remove(i+1)print i
Leads to 0,2,4 being printed (showing the loop runs 3 times).
On the other hand does
it = range(6)
for i in it:it = it[:-2]print it
lead to the output:
[0,1,2,3]
[0,1]
[]
[]
[]
[],
showing the loop runs 6 times. I guess it has something to do with in-place operations or variable scope but cannot wrap my head around it 100% sure.
Clearification:
One example, that doesn't work:
it = range(6)
for i in it:it = it.remove(i+1)print it
leads to 'None' being printed and an Error (NoneType has no attribute 'remove') to be thrown.
When you iterate over a list
you actually call list.__iter__()
, which returns a listiterator
object bound to the list
, and then actually iterate over this listiterator
. Technically, this:
itt = [1, 2, 3]
for i in itt:print i
is actually kind of syntactic sugar for:
itt = [1, 2, 3]
iterator = iter(itt)
while True:try:i = it.next()except StopIteration:breakprint i
So at this point - within the loop -, rebinding itt
doesn't impact the listiterator
(which keeps it's own reference to the list), but mutating itt
will obviously impact it (since both references point to the same list).
IOW it's the same old difference between rebinding and mutating... You'd get the same behaviour without the for
loop:
# creates a `list` and binds it to name "a"
a = [1, 2, 3]
# get the object bound to name "a" and binds it to name "b" too.
# at this point "a" and "b" both refer to the same `list` instance
b = a
print id(a), id(b)
print a is b
# so if we mutate "a" - actually "mutate the object bound to name 'a'" -
# we can see the effect using any name refering to this object:
a.append(42)
print b# now we rebind "a" - make it refer to another object
a = ["a", "b", "c"]
# at this point, "b" still refer to the first list, and
# "a" refers to the new ["a", "b", "c"] list
print id(a), id(b)
print a is b
# and of course if we now mutate "a", it won't reflect on "b"
a.pop()
print a
print b