【问题标题】:Why iterative loop to remove items in list stops为什么迭代循环删除列表中的项目停止
【发布时间】:2015-08-19 12:42:34
【问题描述】:

Python 新手,试图了解这个旨在从列表中删除所有项目的迭代循环是如何处理列表中的索引的,以及为什么它会在它所在的位置停止...

为什么会这样循环:

foo = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
for i in foo:
    foo.remove(i)
    print foo

到此为止?

['b', 'd', 'f', 'h']

而不是在这里?

['H']

另外,这里的索引“幕后”发生了什么?

在每次迭代中,Python 是否会跟踪下一个索引,而同时,一旦删除了一个项目,其右侧的项目将向左移动一个索引(这就是它跳过所有其他项目的原因)?

【问题讨论】:

  • 经验法则:在迭代时不要修改它们。它几乎永远不会起作用。
  • 在更改正在迭代的数据时必须小心。

标签: list python-2.7 loops indexing


【解决方案1】:

要了解发生这种情况的原因,让我们逐步了解内部发生的情况。

第 1 步:

>>> foo = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']

这里创建了一个新的列表对象并分配给foo

第 2 步:

>>> for i in foo:

现在,迭代开始。 i 循环变量被赋值为 index 0 处的 item 的值,即 'a'

第 3 步:

>>> foo.remove(i)
>>> print foo
['b', 'c', 'd', 'e', 'f', 'g', 'h']

现在,.remove(i) 执行 .remove(foo[0]) 而不是 .remove('a') 显然。新列表现在在索引 0 处有 'b',在索引 1 处有 'c',依此类推。

第 4 步:

>>> for i in foo:

对于下一次迭代,i 循环变量被赋值为索引 1 处的项目的值,当前为 'c'

第 5 步:

>>> foo.remove(i)
>>> print foo
['b', 'd', 'e', 'f', 'g', 'h']

这一次,.remove(i) 执行.remove(foo[1]),将'c' 从列表中删除。当前列表现在在索引 0 处有 'b',在索引 1 处有 'd',依此类推。

第 6 步:

>>> for i in foo:

对于下一次迭代,i 循环变量被赋值为 index 2 处的 item 的值,当前为 'e'

第 7 步:

>>> foo.remove(i)
>>> print foo
['b', 'd', 'f', 'g', 'h']

这一次,.remove(i) 执行.remove(foo[2]),将'e' 从列表中删除。同样,项目的索引也会像上面的第 5 步那样更改。

第 8 步:

>>> for i in foo:

对于下一次迭代,i 循环变量被赋值为 index 3 处的 item 的值,当前为 'g'

第 9 步:

>>> foo.remove(i)
>>> print foo
['b', 'd', 'f', 'h']

这一次,.remove(i) 执行 .remove(foo[3]),将 'g' 从列表中删除。

第 10 步:

>>> for i in foo:

现在,i 应该指向 索引 4 处的项目,但由于原始列表已减少到 4 个元素,执行将在此处停止。

>>> foo
['b', 'd', 'f', 'h']

以上是执行后的最终列表。

一些结论:

  • 在迭代列表时不要更改列表的长度。简单来说,不要在迭代时修改原始列表。

  • 在列表中迭代执行.remove() 时,循环变量将使用索引而不是原始列表中的实际项目来引用列表项。

【讨论】:

  • 谢谢拉胡尔!这是一个很棒的解释,确实很有帮助!
【解决方案2】:

它从索引零开始,删除那里的“A”。然后它移动到索引一,删除那里的“D”。 (不是“C”,因为此时它的索引为零。)然后列表中只剩下两个项目,所以它不能移动到索引二,循环结束。

也许您可以使用while 循环代替for 循环,直到列表为空为止。

foo = ['A', 'C', 'D', 'E']
while foo:
    foo.pop(0)
    print foo

... 或者您可以遍历列表的副本,当您修改foo 时,它不会从您下方更改。当然,这会使用一些额外的内存。

foo = ['A', 'C', 'D', 'E']
for i in foo[:]:
    foo.remove(i)
    print foo

【讨论】:

  • 好的,谢谢!使用 enumerate() 函数可以解决这个问题,对吧?
  • 我认为 enumerate 实际上会出现与您的原始代码相同的问题。
  • 你是对的......你能进一步解释为什么循环停止列表 ['b', 'd', 'f', 'h'] 而不是 ['h'] 吗?是不是因为它“索引用完了”?
  • 是的,即使是这个新的扩展示例,原理也是一样的。它会跳过所有其他项目,因为索引每次迭代都会向前移动一个,即使元素被您的 remove 调用向左移动。
  • 好的,即使列表中还剩下一些项目,Python 会说,我们没有索引,所以迭代停止了吗?有没有一种方法可以执行此循环,其中 Python 会跟踪项目以及它们被移动的位置以及索引,这样它只会返回列表中的最后一个项目?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-30
  • 2019-01-10
  • 2012-01-07
  • 2022-07-14
  • 2010-11-23
  • 2014-10-26
  • 1970-01-01
相关资源
最近更新 更多