for循环中的python变量的范围

fir*_*ast 34 python scope for-loop

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

for i in range (0,10):
    if i==5:
        i+=3
    print i
Run Code Online (Sandbox Code Playgroud)

我期望输出为:

0
1
2
3
4
8
9
Run Code Online (Sandbox Code Playgroud)

然而翻译吐出:

0
1
2
3
4
8
6
7
8
9
Run Code Online (Sandbox Code Playgroud)

我知道for循环为C中的变量创建了一个新的作用域,但不知道python.任何人都可以解释为什么python i中的for循环中没有变化的值,并且为了获得预期的输出有什么补救措施.

Jun*_*uxx 36

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

您可以使用while循环获得所需的行为.

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
Run Code Online (Sandbox Code Playgroud)


hug*_*own 19

与C代码类比

你在想象你for-loop的python就像这个C代码:

for (int i = 0; i < 10; i++)
    if (i == 5)
        i += 3;
Run Code Online (Sandbox Code Playgroud)

它更像是这个C代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

因此i,在循环中进行修改不会产生预期的效果.

反汇编示例

你可以看一下python代码反汇编来看看:

>>> 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        
>>> 
Run Code Online (Sandbox Code Playgroud)

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

          3 LOAD_GLOBAL              0 (range)
          6 LOAD_CONST               1 (0)
          9 LOAD_CONST               2 (10)
         12 CALL_FUNCTION            2
Run Code Online (Sandbox Code Playgroud)

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

会在堆栈顶部的对象上获取迭代器,即范围:

         15 GET_ITER  
Run Code Online (Sandbox Code Playgroud)

此时,堆栈顶部包含一个超出实现范围的迭代器.

FOR_ITER开始使用estack顶部的迭代器迭代循环:

    >>   16 FOR_ITER                36 (to 55)
Run Code Online (Sandbox Code Playgroud)

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

在这里你可以看到堆栈的顶部被弹出并分配给i:

         19 STORE_FAST               0 (i)
Run Code Online (Sandbox Code Playgroud)

所以i会不管你在循环做覆盖.

如果您以前没有看过这个,那么这里是堆栈机器概述.


Cla*_*diu 14

Python中的for循环实际上是for-each循环.在每个循环的开始,i设置为迭代器range(0, 10)中的下一个元素(在您的情况下).i在每个循环的开头重新设置的值,因此在循环体中更改它不会更改其下一次迭代的值.

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

_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
Run Code Online (Sandbox Code Playgroud)


Jon*_*nts 5

如果出于某种原因,您确实想将 add 3 更改为i等于5,并跳过下一个元素(这有点推进 C 3 元素中的指针),那么您可以使用迭代器并消耗其中的一些位:

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
Run Code Online (Sandbox Code Playgroud)