为什么修改迭代变量不会影响后续迭代?
- 2024-12-18 08:39:00
- admin 原创
- 145
问题描述:
以下是我遇到问题的 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 中循环会为变量创建一个新的作用域,但对 Python 一无所知。为什么 Python 中循环中for
的值不会改变?如何解决才能获得预期的输出?i
`for`
请参阅如何在 for 循环期间修改列表条目?以了解如何修改原始序列。在 3.x 中,当然range
会创建一个不可变对象,因此它仍然不起作用。
解决方案 1:
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
解决方案 2:
与 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
此时,堆栈顶部包含一个覆盖已实现范围的迭代器。
FOR_ITER使用堆栈顶部的迭代器开始迭代循环:
>> 16 FOR_ITER 36 (to 55)
此时,堆栈顶部包含迭代器的下一个值。
这里你可以看到栈顶被弹出并被分配给i
:
19 STORE_FAST 0 (i)
因此i
无论您在循环中做什么,它都会被覆盖。
如果您以前没有见过,这里是堆栈机的概述。
解决方案 3:
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
解决方案 4:
如果由于某种原因你确实想i
在它等于时将加 3 更改为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
解决方案 5:
在 python 2.7 中,range 函数创建一个列表,而在 python 3.x 版本中,它创建一个“range”类对象,该对象仅可迭代而不是列表,类似于 python 2.7 中的 xrange。
当您迭代范围(1,10)时则不然,最终您将从列表类型对象中读取,并且每次到达循环时 i 都会获取新值。
这是这样的:
for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
if i==5:
i+=3
print(i)
改变值不会改变列表中的迭代顺序。
解决方案 6:
每次迭代时,I 都会被重置,因此在循环内对它做什么其实并不重要。它唯一起作用的时候是当 i 为 5 时,然后它会将 3 加到它上面。一旦它循环回来,它就会将 i 重新设置为列表中的下一个数字。您可能想while
在这里使用 a。
解决方案 7:
Python 的for
循环只是循环遍历所提供的值序列 — 可以将其视为“foreach”。因此,修改变量不会影响循环执行。
教程中对此进行了很好的描述。
解决方案 8:
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
解决方案 9:
您可以对循环进行以下修改for
:
for i in range (0,10):
if i in [5, 6, 7]:
continue
print(i)
解决方案 10:
在我看来,类似的代码不是 while 循环,而是在运行时编辑列表的 for 循环:
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