如何在Python的for循环中删除列表元素?

129

我有一个列表。

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

我想在一个for循环中删除这个列表中的元素,就像下面这样:

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

但是它不起作用。我该怎么办?


2
为什么你需要同时删除它们?只需遍历然后删除整个列表即可。另外,你真的需要打印每个项目吗? - jamylak
但是当我迭代循环时,我依赖于列表中的项目。如果它符合条件,我必须立即将其删除。 - alwbtc
6个回答

183

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

重写代码的最佳方式取决于您要完成什么任务。

例如,您的代码等效于:

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

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

while a:
    print(a.pop())

我试图根据条件删除项目,然后转到下一个项目。

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

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 ...]

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


1
我正在尝试根据条件删除项目,然后继续下一个项目。 - alwbtc
21
“你没有被允许” - 当然可以。Python 不会向您抱怨,它只会给出略微(读作:完全)不同于人们预期的结果。 - lvc
5
为什么?有人可以解释一下为什么会这样吗?每次迭代都会重新评估列表,对吗?那么问题在哪里? - gamen
7
问题在于Python会保持内部计数器以记住当前元素,当列表在迭代器下发生更改时,这个计数器将引用一些意想不到的内容。(但是从后往前迭代并删除可能会按预期工作?) - Pascal
2
使用.pop(0)非常低效!它的时间复杂度为O(N)。只需使用pop(),其时间复杂度为O(1)。 - crypdick
显示剩余4条评论

110

遍历列表的副本:

>>> 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']

5
这种方法和原帖作者的方法存在一个问题,就是如果有多个元素相等,它们可能会出现错误。 - NPE
太棒了!这个对我有效! - Ishtiaque Khan
3
我猜这取决于你期望发生什么。如果你期望这段代码将删除列表中所有的“b”实例,那么它不应该出错,因为 list.remove 只会删除一个项目。 - vikki
2
大型列表的内存问题? - PirateApp
1
@aggregate1166877 很好的问题!在这种情况下,如果我们没有遍历副本,那么删除“b”将使item从“b”跳到“d”,因此如果我们想要删除“b”或“c”,那么它将失败。快速解释:https://gist.github.com/alexlouden/9f1ab4354d1c68ae4c1c94126ac51a20 - Alex L
显示剩余2条评论

32

正如其他答案所述,最好的方法涉及创建一个新列表-要么迭代一个副本,要么构造仅包含所需元素的列表,并将其赋回给同一变量。这两种方法的区别取决于您的用例,因为它们对原始列表的其他变量产生不同的影响(或者更确切地说,第一种会影响它们,而第二种则不会)。

如果由于某种原因不能使用副本,则还有一种依赖于理解为什么修改正在迭代的列表会出问题的选项。列表迭代通过保持索引跟踪来工作,在每次循环中将其递增,直到它从列表末尾掉落。因此,如果您在当前索引处(或之前)删除,从该点到结尾的所有内容都会向左移动一个位置。但是迭代器并不知道这一点,实际上跳过下一个元素,因为它现在位于当前索引而不是下一个索引。但是,删除在当前索引之后的元素不会影响事情。

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

总之:

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

然而,通常复制代码更有利于使代码易读。我只是提出这种可能性以保持完整性。


这应该是被接受的答案,而不是解决方法 - Cool guy

7
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”的实例? - NoName

3

创建一个新的列表,并向该列表添加所需元素。在遍历列表时不能删除元素。


1

可能有点晚回答,但我刚发现这个线程,之前我已经为此创建了自己的代码...

    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并将其余部分推移,因此在删除项目时,从最高号码向最低号码删除列表,以便保持列表顺序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接