删除列表中的多个元素(Python)

17

我有一个列表L。

我可以通过以下方式删除元素i:

del L[i]

但如果我有一组非连续索引需要删除怎么办?

I=set([i1, i2, i3,...])

进行中:

for i in I: 
     del L[i]

不会起作用。

有任何想法吗?


1
请删除列表中的多个元素:https://dev59.com/AHRB5IYBdhLWcg3w1Kle - SilentGhost
5个回答

32

请等一分钟,我有一个小问题与这个Religione。-- Eddie Izzard(模仿Martin Luther)

通过反向迭代列表以保留迭代器来进行删除确实是解决此问题的常见方法。但另一种解决方案是将其转化为另一个问题。不要按照某些标准(在您的情况下,索引存在于要删除的索引列表中)从列表中删除项目,而是创建一个新的列表,其中省略了有问题的项目。

L[:] = [ item for i,item in enumerate(L) if i not in I ]

就此而言,你最开始是从哪里得到 I 中的索引的?你可以将获取要移除的索引和构建新列表的逻辑结合起来。假设这是一个对象列表,你只想保留那些通过 isValid 测试的对象:

L[:] = [ item for item in L if item.isValid() ]

这比以下的要简单得多:

I = set()
for i in range(len(L)):
    if not L[i].isValid():
        I.add(i)

for i in sorted(I, reverse=True):
    del L[i]
大部分情况下,我会将任何关于“如何从列表中删除不需要的项”的问题转化为“如何创建一个只包含我想要的项的新列表”的问题。
编辑:根据Alex Martelli在这个问题的回答,将“L =…”更改为“L[:] =…”。

那在大型列表上的扩展性如何? - jmucchiello
1
事实证明,随着列表变得越来越长,这做得更好。请参见此测试代码:http://pastebin.com/f5bf9e3e8。 - PaulMcG
这是实现此操作的方法;请注意,“I” 应该是一个集合,而不是列表。 - Glenn Maynard
即使在第一个示例中,“I”也应该是一个集合,以便总查找时间为“O(n)”。 - u0b34a0f6ae
第二个代码解决方案中的 item.isValid() 得到了 +1。顺便说一句,如果您已经有了一组索引,则不必像第一个代码解决方案中那样使用 enumerate 来创建元组。我更喜欢 [ L[i] for i in xrange(len(L)) if i not in I ]。要完整地了解如何这样做,请参见 https://dev59.com/AHRB5IYBdhLWcg3w1Kle#20589125。在该链接中,我还展示了一个相关的解决方案,如果您有一个要删除的值列表,而不是索引。 - ToolmakerSteve
显示剩余2条评论

9
for i in I:
    del L[i]

这样做是不行的,因为(根据顺序)你可能会使迭代器无效 - 这通常表现为你打算删除的某些项仍然留在列表中。

以它们的索引逆序从列表中删除项目总是安全的。最简单的方法是使用sorted():

for i in sorted(I, reverse=True):
    del L[i]

这是一个好主意。:D我不确定reversed(list(I))是否正确?但是我明白了:按照I中的索引值以相反顺序进行排序,然后再删除。 - Ezequiel
你需要使用list(),因为你不能直接在set上调用reversed()。 - dcrosta
啊,太傻了!我不是想要reversed(list(...)),我是想要sorted(..., reverse=True)。 - dcrosta
我理解你的意思,但是list(I)没有特定的顺序,我说的对吗?难道不应该是reversed(list(I).sorted())吗?或者list(I).sorted(reverse=True)? - Ezequiel
这实际上是O(n^2)(实际上是O(n*m),我想,取决于每个列表的大小),因为每个删除都必须复制该点之后列表的整个内容。 Paul的解决方案可能更像O(m log n),而且同样容易。 - Glenn Maynard
我的时间测试显示,构建新列表的时间复杂度为O(n),而元素删除的时间复杂度为O(n^2),正如Glenn所述的那样。 - PaulMcG

4
您可以按照以下方式使用numpy.delete
import numpy as np
a = ['a', 'l', 3.14, 42, 'u']
I = [1, 3, 4]
np.delete(a, I).tolist()
# Returns: ['a', '3.14']

如果你不介意最终得到一个numpy数组,可以省略.tolist()。这样做还会带来明显的速度提升,使得这个解决方案更具可伸缩性。我没有进行基准测试,但numpy操作是用C或Fortran编写的已编译代码。

1
如果您的原始列表数据可以安全地转换为集合(即所有唯一值且不需要维护顺序),您还可以使用集合操作:
Lset = set(L)
newset = Lset.difference(I)

你也可以尝试使用 Bag/Multiset 进行一些操作,但这可能并不值得努力。对于大多数情况来说,Paul McGuire 的第二个列表推导解决方案肯定是最好的。


0
L = [ item for item in L if L.index(item) not in I ]

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