【问题标题】:How to remove list elements in a for loop in Python? [duplicate]如何在 Python 的 for 循环中删除列表元素? [复制]
【发布时间】:2012-05-26 19:02:02
【问题描述】:

我有一个清单

a = ["a", "b", "c", "d", "e"]

我想在下面的 for 循环中删除此列表中的元素:

for item in a:
    print(item)
    a.remove(item)

但它不起作用。我能做什么?

【问题讨论】:

  • 为什么要同时删除?只需遍历然后删除整个列表。另外,您真的需要打印每个项目吗?
  • 但是当我遍历循环时,我依赖于列表中的项目。如果它符合条件,我必须立即摆脱它

标签: python list


【解决方案1】:

在使用for 循环迭代列表时,不允许从列表中删除元素。

重写代码的最佳方法取决于您要执行的操作。

例如,您的代码相当于:

for item in a:
    print(item)
a[:] = []

或者,您可以使用while 循环:

while a:
    print(a.pop(0))

我正在尝试删除符合条件的项目。然后我转到下一项。

您可以将不匹配条件的每个元素复制到第二个列表中:

result = []
for item in a:
    if condition is False:
        result.append(item)
a = result

或者,您可以使用filter 或列表解析并将结果分配回a

a = filter(lambda item:... , a)

a = [item for item in a if ...]

其中... 代表您需要检查的条件。

【讨论】:

  • 我正在尝试删除符合条件的项目。然后我转到下一项。
  • “你不被允许”——当然可以。 Python 不会抱怨你,它只是给出了与人们期望的稍微(阅读:完全)不同的结果。
  • 为什么?有人可以指出为什么会这样吗?该列表在每次迭代中都会重新评估,对吗?那么有什么问题呢?
  • 问题是python保留了一个内部计数器来记住当前的elelement,当列表从迭代器下发生变化时,它将引用一些意想不到的东西。 (但在删除时从后向前迭代可能会按预期工作?)
  • 使用.pop(0)效率非常低!它的时间复杂度为 O(N)。只需使用时间复杂度 O(1) 的pop()
【解决方案2】:

遍历列表的副本:

>>> a = ["a", "b", "c", "d", "e"]
>>> for item in a[:]:
    print(item)
    if item == "b":
        a.remove(item)

a
b
c
d
e
>>> print(a)
['a', 'c', 'd', 'e']

【讨论】:

  • 这个和 OP 方法的一个问题是,如果有多个比较相等的元素,它们可能会崩溃。
  • 太棒了!这个对我有用!
  • @NPE 我想这取决于你期望发生什么。如果您希望代码删除列表中的所有“b”实例,那么它不应该中断,因为list.remove 只删除了一项。
  • 大列表的内存问题?
  • @aggregate116​​6877 好问题!在这种情况下,如果我们不遍历副本,那么删除“b”将使itemb 跳到d - 所以如果我们想删除“b”或“c”,那么它会失败。快速解释:gist.github.com/alexlouden/9f1ab4354d1c68ae4c1c94126ac51a20
【解决方案3】:

正如其他答案所说,最好的方法是创建一个新列表 - 遍历副本,或者构建一个仅包含您想要的元素的列表并将其分配回同一个变量。这些之间的区别取决于您的用例,因为它们会以不同的方式影响原始列表的其他变量(或者,更确切地说,第一个会影响它们,而第二个不会)。

如果由于某种原因无法选择副本,您确实有另一个选择,该选择依赖于理解为什么修改您正在迭代的列表会中断。列表迭代通过跟踪索引来工作,每次在循环中递增它,直到它掉出列表的末尾。因此,如果您在当前索引处(或之前)删除,则从该点到结尾的所有内容都会向左移动一个位置。但是迭代器不知道这一点,并且有效地跳过了下一个元素,因为它现在位于 current 索引而不是下一个。但是,删除当前索引 之后的内容不会影响任何内容。

这意味着,如果您将列表迭代到前面,如果您删除当前索引处的项目,则它右侧的所有内容都会向左移动 - 但这并不重要,因为您已经处理了所有要当前位置的右侧,并且您正在向左移动 - 左侧的下一个元素不受更改的影响,因此迭代器为您提供了您期望的元素。

TL;DR:

>>> a = list(range(5))
>>> for b in reversed(a):
    if b == 3:
        a.remove(b)
>>> a
[0, 1, 2, 4]

但是,就使代码易于阅读而言,制作副本通常更好。为了完整起见,我只提到这种可能性。

【讨论】:

    【解决方案4】:
    import copy
    
    a = ["a", "b", "c", "d", "e"]
    
    b = copy.copy(a)
    
    for item in a:
        print(item)
        b.remove(item)
    a = copy.copy(b)
    

    工作:为避免更改您正在迭代的列表,您复制a,对其进行迭代并从b 中删除项目。然后将b(更改后的副本)复制回a

    【讨论】:

    • 等等,所以 .remove(...) 可以从“b”中删除实例,即使从技术上讲,传递的实例是“a”的实例?
    【解决方案5】:

    如何创建一个新列表并将您想要的元素添加到该新列表中。遍历列表时不能删除元素

    【讨论】:

      【解决方案6】:

      回答这个问题可能有点晚了,但我刚刚找到了这个帖子,并且我之前已经为它创建了自己的代码......

          list = [1,2,3,4,5]
          deleteList = []
          processNo = 0
          for item in list:
              if condition:
                  print(item)
                  deleteList.insert(0, processNo)
              processNo += 1
      
          if len(deleteList) > 0:
              for item in deleteList:
                  del list[item]
      

      这可能是一个很长的路要走,但似乎运作良好。我创建了第二个列表,其中仅包含与要删除的列表项相关的数字。请注意,“插入”在位置 0 处插入列表项编号并将其余部分推入,因此在删除项时,列表将从最高编号删除回最低编号,因此列表保持顺序。

      【讨论】:

        猜你喜欢
        • 2022-12-13
        • 1970-01-01
        • 2021-10-12
        • 2018-04-02
        • 1970-01-01
        相关资源
        最近更新 更多