for循环中的pythonvariables的作用域

下面的Python代码即时通讯有问题:

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

我预计输出是:

 0 1 2 3 4 8 9 

然而翻译吐出:

 0 1 2 3 4 8 6 7 8 9 

我知道一个for循环为C中的variables创build一个新的范围,但不知道python。 任何人都可以解释为什么i的值没有改变在for循环python和最新的补救措施,以获得预期的输出。

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

您可以通过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 

用C代码类比

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

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

这更像是这个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; } 

所以修改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 >>> 

这部分创build一个介于0到10之间的范围,并实现它:

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

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

这将得到堆栈顶部的对象的迭代器 ,即范围:

  15 GET_ITER 

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

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

  >> 16 FOR_ITER 36 (to 55) 

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

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

  19 STORE_FAST 0 (i) 

所以无论你在循环中做什么, i都会被覆盖。

以下是堆栈机器的概述,如果你以前没有看到过。

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 

如果由于某种原因,你真的想改变,当它等于5时加3,并跳过下一个元素(这是在C 3元素中推进指针的types),那么你可以使用迭代器并消耗一些比特从那个:

 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 
 it = iter(xrange (0,10)) for i in it: if i==4: all(it.next() for a in xrange(3)) print i 

要么

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

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

Python的for循环只是简单地循环所提供的值序列 – 将其视为“foreach”。 因此,修改variables对循环执行没有影响。

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