for循环中的python变量的范围

Scope of python variable in for loop

下面是我遇到问题的python代码:

1
2
3
4
for i in range (0,10):
    if i==5:
        i+=3
    print i

我预计产量为:

1
2
3
4
5
6
7
0
1
2
3
4
8
9

然而,译员吐出:

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
8
6
7
8
9

我知道for循环为C中的变量创建了一个新的作用域,但不知道Python。有人能解释一下为什么python中的for循环中的i的值没有变化,以及如何补救以获得预期的输出。


for循环迭代range(10)中的所有数字,即[0,1,2,3,4,5,6,7,8,9]。更改EDOCX1的当前值(4)对范围内的下一个值没有影响。

您可以通过while循环获得所需的行为。

1
2
3
4
5
6
7
8
9
10
i = 0
while i < 10:
    # do stuff and manipulate `i` as much as you like      
    if i==5:
        i+=3

    print i

    # don't forget to increment `i` manually
    i += 1

类比C码

您想象的是,您在python中的for-loop类似于以下C代码:

1
2
3
for (int i = 0; i < 10; i++)
    if (i == 5)
        i += 3;

它更像是C代码:

1
2
3
4
5
6
int r[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (int j = 0; j < sizeof(r)/sizeof(r[0]); j++) {
    int i = r[j];
    if (i == 5)
        i += 3;
}

所以在循环中修改i并没有您期望的效果。

拆卸示例

您可以查看python代码的反汇编来了解这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
>>> from dis import dis
>>> def foo():
...     for i in range (0,10):
...         if i==5:
...             i+=3
...         print i
...
>>> dis(foo)
  2           0 SETUP_LOOP              53 (to 56)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (0)
              9 LOAD_CONST               2 (10)
             12 CALL_FUNCTION            2
             15 GET_ITER            
        >>   16 FOR_ITER                36 (to 55)
             19 STORE_FAST               0 (i)

  3          22 LOAD_FAST                0 (i)
             25 LOAD_CONST               3 (5)
             28 COMPARE_OP               2 (==)
             31 POP_JUMP_IF_FALSE       47

  4          34 LOAD_FAST                0 (i)
             37 LOAD_CONST               4 (3)
             40 INPLACE_ADD        
             41 STORE_FAST               0 (i)
             44 JUMP_FORWARD             0 (to 47)

  5     >>   47 LOAD_FAST                0 (i)
             50 PRINT_ITEM          
             51 PRINT_NEWLINE      
             52 JUMP_ABSOLUTE           16
        >>   55 POP_BLOCK          
        >>   56 LOAD_CONST               0 (None)
             59 RETURN_VALUE        
>>>

此部分创建0到10之间的范围并实现:

1
2
3
4
          3 LOAD_GLOBAL              0 (range)
          6 LOAD_CONST               1 (0)
          9 LOAD_CONST               2 (10)
         12 CALL_FUNCTION            2

此时,堆栈顶部包含范围。

这将在堆栈顶部的对象上获取一个迭代器,即范围:

1
         15 GET_ITER

此时,堆栈顶部包含一个已实现范围内的迭代器。

对于"Iter",使用位于堆栈顶部的迭代器开始循环迭代:

1
    >>   16 FOR_ITER                36 (to 55)

此时,堆栈顶部包含迭代器的下一个值。

在这里,您可以看到栈顶被弹出并分配给i

1
         19 STORE_FAST               0 (i)

因此,无论您在循环中做什么,i都将被覆盖。

如果您以前没有看到这个,这里是堆栈机器的概述。


python中的for循环实际上是for each循环。在每个循环的开始,i被设置为迭代器中的下一个元素(在您的例子中是range(0, 10))。在每个循环的开始重新设置i的值,因此在循环体中更改该值不会更改其下一次迭代的值。

也就是说,您编写的for循环等价于以下while循环:

1
2
3
4
5
6
7
8
9
10
11
12
_numbers = range(0, 10) #the list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_iter = iter(_numbers)
while True:
    try:
        i = _iter.next()
    except StopIteration:
        break

    #--YOUR CODE HERE:--
    if i==5:
        i+=3
    print i

如果出于某种原因,您真的想在等于5时将add 3改为i,并跳过下一个元素(这是在c3元素中推进指针),那么您可以使用迭代器并从中消耗一些位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from collections import deque
from itertools import islice

x = iter(range(10)) # create iterator over list, so we can skip unnecessary bits
for i in x:
    if i == 5:            
        deque(islice(x, 3), 0) #"swallow up" next 3 items
        i += 3 # modify current i to be 8
    print i

0
1
2
3
4
8
9


在python 2.7 range函数中创建一个列表,而在python 3.x版本中,它创建一个"range"类对象,该对象只能是不可迭代的,而不是列表,类似于python2.7中的xrange。

当您在范围(1,10)内迭代时,并不是这样,最终您将从列表类型对象中读取,并且每次到达for循环时,我都获取新的值。

这类似于:

1
2
3
4
for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
    if i==5:
        i+=3
    print(i)

更改值不会更改列表中的迭代顺序。


1
2
3
4
it = iter(xrange (0,10))
for i in it:
    if i==4: all(it.next() for a in xrange(3))
    print i

1
2
3
4
5
it = iter(xrange (0,10))
itn = it.next
for i in it:
    if i==4: all(itn() for a in xrange(3))
    print i


python的for循环简单地循环所提供的值序列-将其视为"foreach"。因此,修改变量对循环执行没有影响。

这在本教程中有很好的描述。


每次迭代我都会重置,所以在循环中对它做什么并不重要。唯一一次它做任何事情是当我5岁的时候,然后它加3。一旦它循环返回,然后将我设置回列表中的下一个数字。您可能想在这里使用while


您可以对您的for循环进行以下修改:

1
2
3
4
for i in range (0,10):
    if i in [5, 6, 7]:
        continue
    print(i)

在我看来,类似的代码不是while循环,而是for循环,在运行时编辑列表:

1
2
3
4
5
6
7
8
9
10
originalLoopRange = 5
loopList = list(range(originalLoopRange))
timesThroughLoop = 0
for loopIndex in loopList:
    print(timesThroughLoop,"count")
    if loopIndex == 2:
        loopList.pop(3)
        print(loopList)
    print(loopIndex)
    timesThroughLoop += 1