Python For循环列表有趣的结果

时间:2013-01-22 18:35:48

标签: python python-2.7

a = [0,1,2,3,4,5]
for b in a:
  print ":"+str(b)
  a.pop(0)

认为这可以按顺序遍历整个列表及其所有项目,我运行此代码并期望这样做。

:0
0
:1
1
:2
2
:3
3
:4
4
:5
5

相反,我得到了这个:

:0
0
:2
1
:4
2

现在我明白为什么会发生这种情况,但这是python中的错误吗?它不应该仍然通过所有原始对象而不是当前列表的长度?为什么这不会抛出并超出界限错误? IE:不应该这样做了:

:0
0
:1
2
:2
4
:3
Error
:4
Error
:5
Error

4 个答案:

答案 0 :(得分:5)

这完全是“预期的”并记录在案的行为。当您遍历列表时,您基本上是在内存位置上进行迭代。当您从列表中弹出一些内容时,列表中的所有内容会将1个索引移动到更靠近列表开头的位置。因此,您最终会跳过项目。当您到达列表末尾时,迭代停止。

通常在执行此类操作时,您希望迭代列表的副本:

for b in a[:]:
    ...

如评论中所示,如果以相反的顺序迭代列表:

for b in reversed(a):
    a.pop()

这是按预期工作的,因为你不断拉出最后一个元素,因此你不会改变你尚未看到的任何元素列表中的位置。

答案 1 :(得分:4)

您正在遍历列表并同时更改它。通过使用.pop()缩短列表,但迭代器指针不会更新。

改为使用副本:

for b in list(a):

for b in a[:]:

其中[:]切片表示法返回列表副本。

另一种方法是使用while循环代替:

while a:
    print a.pop(0)

因为空列表测试为布尔值False

python for循环使用它的参数作为迭代器,它本身不保留索引。 for循环无法“知道”您删除了元素。相反,它是保持指针的list()迭代器:

>>> a = [0,1,2,3,4,5]
>>> itera = iter(a)
>>> itera.next()  # index 0 -> a[0] is 0
0
>>> a.pop(0)
0
>>> a
[1,2,3,4,5]
>>> itera.next()  # index 1 -> a[1] is 2
2

迭代器保留一个计数器,每次在迭代器上调用next()时,它会在下一个索引处给出值,该值可能,直到计数器为止等于列表的当前长度。

答案 2 :(得分:0)

如果您想在循环中使用.pop(),常见的习惯用法是将其与while一起使用:

a = [0,1,2,3,4,5]
while a:
  print ":{}".format(a.pop(0))

或者,如果您想要印刷图案,那么:

a = [0,1,2,3,4,5]
while a:
    print ":{}\n{}".format(a[0],a.pop(0))

打印

:0
0
:1
1
:2
2
:3
3
:4
4
:5
5

答案 3 :(得分:0)

for循环的每次迭代中,我们必须检查条件b in a所以当你开始时:

a = [0,1,2,3,4,5]
for b in a:
  print ":"+str(b)
  a.pop(0)

b = 0in a(意思是a中的元素)是5。现在打印字符串化版本a[b],然后删除数组的第一个元素。因此,迭代二变为:

a = [1, 2, 3, 4, 5]
b = 1 (it incremented)
size of a = 4 (it shrank)

接下来它们将是b=2,a的大小变为3.最后的迭代会产生你期望的越界错误,因为b会更大比数组的大小,所以我们已经完成了。