Same variable name for different values in nested loops.

前端 未结 6 1075
囚心锁ツ
囚心锁ツ 2020-12-22 12:26

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

相关标签:
6条回答
  • 2020-12-22 12:36

    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

    0 讨论(0)
  • 2020-12-22 12:37

    e is just a label. Each iteration of your outer loop, e is assigned the nth value from x, and each inner loop iteration it is assigned the mth 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.

    0 讨论(0)
  • 2020-12-22 12:45

    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
    
    0 讨论(0)
  • 2020-12-22 12:54

    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))
    
    0 讨论(0)
  • 2020-12-22 12:54

    '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.

    0 讨论(0)
  • 2020-12-22 12:57

    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]
    
    0 讨论(0)
提交回复
热议问题