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

时间:2015-01-04 15:08:41

标签: python numpy nested

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

(a)中

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

4 个答案:

答案 0 :(得分:2)

你的术语有点不对劲。在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]

答案 1 :(得分:1)

我认为这确实是一个变量范围的问题。 ij位于本地函数名称空间中。虽然循环不会创建新的命名空间,所以在代码片段中

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

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

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

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

您将本地函数命名空间中的ij重新分配给for循环中的迭代值。 j相对良性,因为您返回内部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)

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

答案 2 :(得分:0)

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

函数中定义的变量具有函数范围,仅在函数体中可见。 您可以在不同的功能中使用相同的名称。

代码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

答案 3 :(得分:-1)

这说明了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

对于像这样的简单循环,你可以随心所欲地调整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)