为什么修改迭代变量不影响后续迭代?
这是我遇到问题的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
我知道在C语言中,for
循环会为变量创建一个新的作用域,但我对Python不太了解。为什么在Python的for
循环中,i
的值没有变化?有什么办法可以解决这个问题,让我得到预期的输出呢?
想了解如何在for
循环中修改列表的内容,可以查看 如何在for循环中修改列表项? 在Python 3.x中,当然,range
会创建一个不可变的对象,所以这样做也不会有效。
10 个回答
18
在Python中,for循环其实是一个“遍历每个元素”的循环。在每次循环开始时,i
会被设置为迭代器中的下一个元素(在你的例子中是range(0, 10)
)。每次循环开始时,i
的值都会重新设置,所以在循环体内改变i
的值,并不会影响下一次循环中的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
28
与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
>>>
这部分 创建了一个从0到10的范围 并实现了它:
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)
此时,栈顶包含迭代器的下一个值。
在这里你可以看到 栈顶的值被弹出并赋值给 i
:
19 STORE_FAST 0 (i)
所以无论你在循环中做什么, i
都会被覆盖。
如果你之前没有见过,这里有一个 栈机器的概述。
58
这个for循环会遍历从0到9的所有数字,也就是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