简体   繁体   English

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

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

What happens to for i in range() when iterator is overwritten in nested while loop? 在嵌套的while循环中覆盖迭代器时for i in range()会发生什么情况? For example, why do the following snippets give different output? 例如,为什么以下代码片段给出不同的输出? When I change the name of the variables i and j inside the while loop, the snippet behaves as I expected. 当我在while循环中更改变量ij的名称时,该片段的行为与我预期的一样。 However, when the while loop overwrites i and j , the for loop is affected. 但是,当while循环覆盖ijfor循环会受到影响。 Is the resulting behavior of the for loop when its iterator is overwritten in the while predictable? while迭代器的迭代器被覆盖时, for循环的结果行为是否可以预测?

(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

has o/p: 具有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) (the same code with the while commented) (B)(具有相同的代码while注释)

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

has o/p 有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

Your terminology is slightly wrong. 您的术语有些错误。 In a for loop you have no access to an iterator. 在for循环中,您无权访问迭代器。 The iterator is kept hidden, behind the scenes. 迭代器被隐藏在幕后。 The following looping structures are equivalent. 以下循环结构是等效的。

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

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

As you can see the iterator object ( it ) is kept hidden in the for loop. 如您所见,迭代器对象( it )被隐藏在for循环中。 It's possible to expose the iterator in a for loop, but that's a different question. 可以在for循环中公开迭代器,但这是一个不同的问题。

What you are talking about is the name that the elements of the iterable are stored in. If you write over that name during the course of your loop, then that value will simply be ignored at the start of the next iteration of the loop. 您要说的是存储iterable元素的名称。如果在循环过程中覆盖该名称,则在循环的下一次迭代开始时将简单地忽略该值。 This is easy to see in the while version of the looping structure where the first thing that is done is that the name i is assigned the next element returned by the iterator.whil 在循环结构的while版本中,这很容易看到,其中要做的第一件事是为名称i分配了iterator.whil返回的下一个元素。

I'm not sure of the purpose of your code, but it is possible to change the state of the iterator you are using. 我不确定您的代码的用途,但是可以更改您正在使用的迭代器的状态。 To do this you must write a coroutine . 为此,您必须编写一个协程 A coroutine is a specialised generator that is able to accept input. 协程是一种能够接受输入的专用生成器。

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

For simple range usages the generator version acts the same. 对于简单的范围用法,生成器版本具有相同的作用。

eg. 例如。

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))

But we can do more complicated looping algorithms with the coroutine. 但是我们可以使用协程执行更复杂的循环算法。

eg. 例如。

# 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]

I think this is really a question of variable scope. 我认为这确实是一个范围可变的问题。 i and j are in the local function namespace. ij在本地函数名称空间中。 While loops do not create new namespaces, so in the code fragment While循环不会创建新的名称空间,因此在代码片段中

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

i and 'j' are still the same variables in the local namespace and all you did was reassign them to themselves. i和'j'在本地名称空间中仍然是相同的变量,您所做的只是将它们重新分配给了它们自己。 You didn't create new i or j . 您没有创建新的ij Its harmless, but those assignments should be removed. 它无害,但应删除这些任务。

Later, at the bottom of the while loop, when you do 稍后,在while循环的底部,当您执行

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

You reassign the i and j in the local function namespace that were holding the iterated values from the for loops to something else. 您将本地函数名称空间中的ij重新分配,这些ij保留了从for循环到其他内容的迭代值。 The is relatively benign for j because you return to the inner for loop and j is reassigned the next iterated value. 对于j而言,相对而言是良性的,因为您返回了内部for循环,并且为j重新分配了下一个迭代值。 But it's a problem for x because it will hold the changed values until the outer for is reached again. 但这对于x来说是个问题,因为它将保存更改后的值,直到再次达到外部for

You can see the problem clearly with a couple of print statements 您可以通过几个打印语句清楚地看到问题

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)

The solution is to use different variable names. 解决方案是使用不同的变量名。

If I am clear about your question, then here is solution: 如果我很清楚你的问题,那么这里是解决方法:

Variables defined in a function have Function Scope and are only visible in the body of the function. 在函数中定义的变量具有函数作用域,并且仅在函数主体中可见。 You can use same name in different function. 您可以在不同的功能中使用相同的名称。

Code 1: 代码1:

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

VAR1()

Output: 输出:

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

But in a single function, variables are local. 但是在一个函数中,变量是局部的。 You can't use same name in different place 您不能在其他地方使用相同的名称

Code 1: 代码1:

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

VAR()

Output: 输出:

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

Read more Python's namespaces, scope resolution 阅读更多内容Python的名称空间,范围解析

This illustrates what is going on in your case(a) : 这说明了您的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)

With output: 输出:

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

There's nothing special about the i and j variables, except that they get new values at the start of their respective loops. ij变量没有什么特别的,除了它们在各自的循环开始时获得新值。 And after the loops, i and j will have their last values within the loops, in this case new-i and new-j . 在循环之后, ij将具有循环内的最后一个值,在这种情况下为new-inew-j

For simple loops like this, you can fiddle with the the value of i all you want, and it won't mess up the iteration (that is, the action of the for statement). 对于这样的简单循环,您可以摆弄i想要的i的值,并且不会弄乱迭代(即for语句的操作)。 But since it can confuse you and your algorithm (and your readers), it is generally not a good idea to reassign for iteration variables. 但是,因为它可以迷惑你和你的算法(和你的读者),所以一般不重新分配一个好主意, for迭代变量。


To fully separate your iteration variables from changes within the the while loop, you need to define a function like this: 为了将迭代变量与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)

producing: 生产:

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

If on the other hand, you want changes in the inner while to control the iteration of i you need to use while in the outer loop as well: 如果在另一方面,你要在内部的变化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

which only prints once ( i and j both jump to 3) 仅打印一次( ij都跳到3)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM