【问题标题】:Modifying a list while iterating over it with python在使用 python 迭代列表时修改列表
【发布时间】:2018-10-12 03:34:20
【问题描述】:

编辑:当我想修改原始列表时,我知道要遍历列表的副本。但是,我收到的关于在迭代列表时修改列表有什么问题的唯一解释是“它可能导致意外结果”。

考虑以下几点:

lst = ['a', 'b', 'c', 'd', 'e']
for x in lst:
    lst.remove(x)
print(lst)

这是我试图解释在迭代列表时修改列表时实际发生的情况。请注意,line2 等价于for i in range(len(lst)):,并且len(lst) 每次迭代都会减 1。

len(lst) 以 5 开头。

i = 0 时,我们删除了lst[i] = 'a',所以lst = ['b', 'c', 'd', 'e']len(lst) 减少到 4。

i = 1,我们有lst[i] = 'c'被删除,所以lst = ['b', 'd', 'e'] len(lst) 减少到 3。

i = 2 时,我们删除了lst[i] = 'e',所以lst = ['b', 'd']len(lst) 减少到 2。

这是我认为会引发 IndexError 的地方,因为 i = 2 不在 range(2) 中。但是,程序只是输出['b', 'd']。是因为i“赶上了”len(lst)吗?另外,到目前为止我的推理是否合理?

【问题讨论】:

  • 复制您的列表,并在该副本上使用您的索引。
  • @jozzas 她在问迭代是如何工作的。我没有看到它回答了您提到的问题。
  • @BcK 我的目的不是清除列表;我只是想了解在后台发生了什么。
  • Removing from a list while iterating over it 的可能副本此副本的最佳答案讨论(具有良好的可视化)这里发生的事情。它略有不同,因为并非所有元素都被删除,但我认为足够接近以成为骗子。

标签: python python-3.x iteration


【解决方案1】:

C 实现在listobject.clistiter_next 函数中,相关行是

if (it->it_index < PyList_GET_SIZE(seq)) {
    item = PyList_GET_ITEM(seq, it->it_index);
    ++it->it_index;
    Py_INCREF(item);
    return item;
}

it->it_seq = NULL;
Py_DECREF(seq);
return NULL;

如果对象仍在范围内 (it-&gt;it_index &lt; PyList_GET_SIZE(seq)),则迭代器返回一个对象,否则返回 NONE。不管你偏离 1 还是 100 万,这都不是错误。

这样做的一般原因是迭代器和可迭代对象可以在多个地方使用(考虑在 for 循环中读取的文件对象)。外循环不应该因为没有事情可做而与IndexError 一起崩溃。更改您正在迭代的对象并不违法或天生“愚蠢”,只是您需要知道您的行为的后果。

【讨论】:

    【解决方案2】:

    "注意line2等价于for i in range(len(lst))"

    我觉得不是
    Python 中的 for 循环使用集成的 next 函数遍历列表。所以最后你会得到一个停止迭代错误,如果你正在迭代的迭代完成,则由 next 引发。但是这个错误会被 for 循环自动捕获。

    【讨论】:

    • 你能解释一下为什么等价不成立吗?这是我从你的第二段中收集到的:当i = 2 时,会引发 StopIteration 异常,因为i 不能进一步增加(因为i 不能超过range(len(lst)))。 StopIteration 导致程序退出 for 循环并因此终止的原因。对吗?
    【解决方案3】:

    你应该能够知道你是否在这个过程中打印了 x,

    lst = [1, 2, 3, 4, 5]
    for x in lst:
        print(x)
        lst.remove(x)
    
    # 1
    # 3
    # 5
    

    发生的情况是,您首先从列表中删除了 1。因为您删除了 1,而不是继续执行 2,而是继续执行 3。然后从列表中删除 3。现在同样的程序适用,而不是继续到数字 4,而是继续到数字 5 并从列表中删除该数字。所以你已经完成了你的迭代。

    顺便说一句,for x in lstfor x in range(len(lst)) 不一样,这可能是你感到困惑的地方。

    在第一种情况下,python 从您的列表中创建一个可迭代对象,并在每次迭代时调用next 方法,因此当您到达列表末尾时,会引发StopIteration 错误,导致迭代过程停止.在第二种情况下,您应该自己明确处理。这意味着,python 不会从您的列表中创建可迭代对象,您应该跟踪自己的位置。


    我建议您阅读这篇文章以了解可迭代和迭代器之间的区别以及它们的工作原理:

    Iterator vs Iterable

    【讨论】:

    • “因为你删除了一个,而不是继续到 2,你继续到 3”。如果我的解释不正确,我不明白为什么会发生这种情况。你能解释一下为什么for x in lstfor i in range(len(lst))不一样吗?
    • @jessica 进一步澄清了答案。
    猜你喜欢
    • 2014-09-17
    • 2020-09-07
    • 1970-01-01
    • 2017-12-05
    • 1970-01-01
    • 2023-03-06
    • 2020-07-28
    • 1970-01-01
    • 2019-04-04
    相关资源
    最近更新 更多