当迭代器在嵌套的 while 循环中被覆盖时,`for i in range()` 会发生什么?

What happens to `for i in range()` when iterator is overwritten in nested while loop?

当迭代器在嵌套的 while 循环中被覆盖时 for i in range() 会发生什么?例如,为什么以下代码片段会给出不同的输出?当我在 while 循环中更改变量 ij 的名称时,代码片段的行为与我预期的一样。但是,当 while 循环覆盖 ij 时,for 循环会受到影响。 for 循环的迭代器在 while 中被覆盖时的结果行为是否可预测?

(一)

for i in range (0,3):
    for j in range (0,3):
         print "after nested for i,j",i,j
         counter = 0
         while counter < 3:
                counter += 1
                i = counter
                j = counter

有o/p:

after nested for i,j 0 0
after nested for i,j 3 1
after nested for i,j 3 2
after nested for i,j 1 0
after nested for i,j 3 1
after nested for i,j 3 2
after nested for i,j 2 0
after nested for i,j 3 1
after nested for i,j 3 2

(b)(与 while 注释相同的代码)

for i in range (0,3):
    for j in range (0,3):
         print "after nested for i,j",i,j

有o/p

after nested for i,j 0 0
after nested for i,j 0 1
after nested for i,j 0 2
after nested for i,j 1 0
after nested for i,j 1 1
after nested for i,j 1 2
after nested for i,j 2 0
after nested for i,j 2 1
after nested for i,j 2 2

如果我很清楚你的问题,那么这里是解决方案:

函数中定义的变量具有函数作用域,并且只在函数体中可见。 您可以在不同的功能中使用相同的名称。

代码 1:

def VAR1():
    var = 'foo'
    def inner():
        var = 'bar'
        print 'inside function, var is ', var
    inner()
    print 'outside function, var is ', var

VAR1()

输出:

inside function, var is  bar
outside function, var is  foo

但是在单个函数中,变量是局部的。你不能在不同的地方使用相同的名字

代码 1:

def VAR():
    var = 'foo'
    if True:
        var = 'bar'
        print 'inside if, var is ', var
    print 'outside if, var is ', var

VAR()

输出:

inside if, var is  bar
outside if, var is  bar

阅读更多Python's namespaces, scope resolution

您的术语有点错误。在 for 循环中,您无权访问迭代器。迭代器隐藏在幕后。以下循环结构是等价的。

for i in range(10):
    print(i)

it = iter(range(10)):
while True:
    try:
        i = next(it)
    except StopIteration:
        break
    print(i)

如您所见,迭代器对象 (it) 隐藏在 for 循环中。可以在 for 循环中公开迭代器,但这是一个不同的问题。

您所说的是存储可迭代元素的名称。如果您在循环过程中重写该名称,那么在下一次迭代开始时该值将被忽略的循环。这在循环结构的 while 版本中很容易看出,其中所做的第一件事是将名称 i 分配给 iterator.whil

返回的下一个元素

我不确定您的代码的用途,但可以更改您正在使用的迭代器的状态。为此,您必须写一个 coroutine。协程是一种能够接受输入的专用生成器。

def generator_range(start, end, step=1):
    "Simplified version of the range/xrange function written as a generator."
    counter = start
    while counter < end:
        yield counter 
        counter += step

def coroutine_range(start, end, step=1):
    "Special version of range that allows the internal counter to set."
    counter = start
    while counter < end:
        sent = yield counter 
        if sent is None:
            counter += step
        else:
            counter = sent

对于简单范围的使用,生成器版本的行为相同。

例如

assert list(range(0, 10)) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert list(range(0, 10)) == list(generator_range(0, 10))
assert list(range(0, 10)) == list(coroutine_range(0, 10))

但是我们可以用协程做更复杂的循环算法。

例如

# skip numbers in range 3-7 inclusive
l = []
co = coroutine_range(0, 10)
item_to_send = None
while True:
    try:
        i = co.send(item_to_send)
        # if item_to_send is None then the above is the same as next(co)
        item_to_send = None
    except StopIteration:
        break
    if 3 <= i <= 7:
        item_to_send = 8
    else:
        l.append(i)

assert l == [0, 1, 2, 8, 9]

我认为这确实是一个变量范围的问题。 ij 在局部函数命名空间中。 while 循环不创建新的命名空间,所以在代码片段

     while len(list) < 2:
            i = i
            j = j

i 和 'j' 仍然是本地命名空间中的相同变量,您所做的只是将它们重新分配给自己。您没有创建新的 ij。它无害,但应删除这些分配。

稍后,在 while 循环的底部,当您执行

            i = PixelCoord[Lightest[1]][0]
            j = PixelCoord[Lightest[1]][1] 

您将保存 for 循环中的迭代值的局部函数命名空间中的 ij 重新分配给其他对象。 j 相对良性,因为您 return 到内部 for 循环并且 j 被重新分配到下一个迭代值。但这对 x 来说是个问题,因为它将保持更改的值,直到再次到达外部 for

你可以通过几个打印语句清楚地看到问题

for i in range (1,array.shape[0]-1):
    print('outer for, i is', i)
    for j in range (1,array.shape[1]-1):
        print('inner for, i and j are', i, j)

解决方法是使用不同的变量名。

这说明了您的 case(a):

中发生了什么
for i in range(0,2):
    for j in range(0,2):
        print(i,j)
        i = 'new-i'
        j = 'new-j'
        print('   ', i,j)

输出:

0 0                 # i and j set by the for statements
    new-i new-j     # i and j set by the inner assignments
new-i 1             # new j set by its for; i unchanged
    new-i new-j
1 0                 # new i and j set by for
    new-i new-j
new-i 1
    new-i new-j

ij 变量没有什么特别之处,只是它们在各自的循环开始时获得新值。在循环之后,ij 将在循环中具有它们的最后一个值,在本例中为 new-inew-j.

对于像这样的简单循环,您可以 fiddle 随心所欲地使用 i 的值,并且不会弄乱迭代(即 for语句)。但是因为它会让你和你的算法(以及你的读者)感到困惑,重新分配 for 迭代变量通常不是一个好主意。


要将迭代变量与 while 循环中的更改完全分开,您需要定义一个函数,如下所示:

def foo(i,j):
     print("after nested for i,j",i,j)
     counter = 0
     while counter < 3
         counter += 1
         i = counter
         j = counter

for i in range(3):
    for j in range(3):
        foo(i,j)

生产:

after nested for i,j 0 0
after nested for i,j 0 1
...
after nested for i,j 2 1
after nested for i,j 2 2

另一方面,如果您希望更改内部 while 来控制 i 的迭代,您还需要在外部循环中使用 while

i = 0
while i<3:
    j = 0
    while j<3:
        print("after nested for i,j",i,j)
        counter = 0
        while counter < 3:
            counter += 1
            i = counter
            j = counter

只打印一次(ij 都跳到 3)