This code is perfectly valid Python
x=[[1,2,3,4], [11,22,33,44]]
for e in x:
for e in e:
print e
Can someone please tell me why
Frankly, I did not find any of the current answers to this question to be satisfying. I think the underlying explanation is that Python handles for-loop scoping as a special case.
It looks like Python recognizes that a for-loop variable is special and, at the conclusion of the for-loop, copies the current value of the variable to the enclosing scope, as needed.
This explains why you can write the following code:
for i in range(3):
i = 5
print("hello")
and it will execute 3 times. The for-loop variable i is special and distinct from the variable i that is assigned the value 5.
Python probably sees it as something like:
for i_12349678 in range(3):
i = 5
print("hello")
And for nested for-loops that use the same variable i:
for i in range(3):
for i in range(4):
print("i = %s" % i)
Python probably sees it more like:
for i_18987982221 in range(3):
for i_9870272721 in range(4):
print("i = %s" % i_9870272721)
For more information: http://mail.python.org/pipermail/python-ideas/2008-October/002109.html
e
is just a label. Each iteration of your outer loop, e
is assigned the n
th value from x
, and each inner loop iteration it is assigned the m
th value from x[n]
. It's perfectly valid Python code, it is just not advisable from a style perspective because outside of a trivial example, it can quickly become confusing what e
represents at what point in the code, and thus is likely to lead to bugs.
The scopes aren't different; in Python a function has a single local scope (as does code entered at global level in the console).
The reason the code is OK is that you finish using the outer value of e
before you rebind it to the inner values; try looking at what this prints:
x=[[1,2,3,4], [11,22,33,44]]
for e in x:
for e in e:
print e
print e
I suppose you could roughly translate the inner loop in that above expression to:
for e in x:
ee = iter(e)
try:
e = next(ee)
while True
print e
e = next(ee)
except StopIteration
pass
Note that the key thing here is in the statement: for e in ...
, ...
is converted to an iterator via the iterator protocol. The object you actually iterate over is a separate object from the e
you gave it initially. Since it's a separate object (stored separately from its name in the current scope to allow it to be iterated over) there is no problem with binding a new variable to that name in the current scope -- Maybe I should say that there is no problem other than it makes the code really hard to follow.
It's effectively the same reason you don't have a problem doing this:
A = [['foo']] #Define A
b = A[0] #Take information from A and rebind it to something else
c = A #We can even take the entire reference and bind/alias it to a new name.
A = 'bar' #Re-assign A -- Python doesn't care that A already existed.
Here are a couple more things to think about:
x = [1,2,3,4]
for a in x:
print a
next(a) #Raises an error because lists aren't iterators!
Now a seldom used, (but sometimes necessary) idiom:
x = [1,2,3,4]
y = iter(x) #create an iterator from the list x
for a in y:
print a
#This next line is OK.
#We also consume the next value in the loop since `iter(y)` returns `y`!
#In fact, This is the easiest way to get a handle on the object you're
#actually iterating over.
next(y)
finally:
x = [1,2,3,4]
y = iter(x) #create an iterator from the list x
for a in y:
print a
#This effectively does nothing to your loop because you're rebinding
#a local variable -- You're not actually changing the iterator you're
#iterating over, just as `A = 'bar'` doesn't change the value of
#the variable `c` in one of the previous examples.
y = iter(range(10))
'e' in this case are in the same scope. If you do something like...
for i in range(MAX):
if(some_condition == True):
for i in range(5):
#do stuff
If the code traverses into the inner for loop, it'll increment the "outer i" 5 times, causing you to skip out on those runs.
With the code you posted, it doesn't produce a syntax error, and it happens to work out from a logical perspective, but there may be other examples where you will get the wrong result.
Because second e
binds after first e
evaluates to list. So, all other iteration steps take items not from variable, but from list. For example, in next code reassinging to e
has no effect on iteration:
for e in x:
for i in e:
print i
e = [8, 8, 8]