在Python中从列表中删除多个项目

16
因此,例如,我有一个列表:myList=["asdf","ghjk","qwer","tyui"]
我还有一个要删除项的索引号列表:removeIndexList=[1,3](我想从上面的列表中删除项目1和3)
最好的方法是什么?

3
顺便提一下,不要把你的列表命名为 list,那会隐藏内置类型/构造函数。 - abarnert
我问这个问题是因为可能有更好的方法来完成它,而不是制作一个看起来像胶带一样的循环。我使用那个名称来命名列表,因为这只是一个示例,我认为这并不是什么大问题。 - mid_kid
2
即使您不喜欢自己编写的方式,也请向我们展示您编写的方式。首先,这意味着如果您的问题细节不是完全清楚的,我们可以运行您的代码以找出您的意图。其次,它给我们一个了解您有多少经验的想法,因此我们可以针对性地回答问题。最后,许多人甚至不会看那些没有展示任何工作的问题,认为您只是懒惰;您这次得到了一个好答案,但如果您吓跑了一半的潜在回答者,您就必须要幸运两倍... - abarnert
2
这不是一个“帮我修复代码”的问题,而是一个“实现x的最佳方式是什么”的问题。我只告诉你我的代码看起来像“胶带”是因为你问了。除此之外,它是无关紧要的。不,我不懒惰。我正在寻找一个真正的问题,已经搜索了大约30分钟的答案。我在问题中放置了每一个必要的信息,以便回答。你为什么认为一段代码会在这种情况下有所帮助呢?如果有什么作用,我认为它只会占用空间。 - mid_kid
我并不是说你懒惰,我是在说这里(以及许多其他地方)有一半的人会不公平地“假设”你是懒惰的,并跳过你的问题,使你只有一半的可能性得到一个好答案。你可能仍然会幸运,就像你现在得到了答案,但为什么要依赖于这种运气呢? - abarnert
显示剩余2条评论
2个回答

21

使用带有 enumerate() 的列表推导式:

newlist = [v for i, v in enumerate(oldlist) if i not in removelist]

removelist改为set类型可以加快速度:

removeset = set(removelist)
newlist = [v for i, v in enumerate(oldlist) if i not in removeset]

演示:

>>> oldlist = ["asdf", "ghjk", "qwer", "tyui"]
>>> removeset = set([1, 3])
>>> [v for i, v in enumerate(oldlist) if i not in removeset]
['asdf', 'qwer']

len(removelist)为2时,我怀疑集合并没有起到帮助作用。特别是当len(list)只有4时。事实上:%timeit [v for i, v in enumerate(a) if i not in r]给出了1.15微秒;%timeit [v for i, v in enumerate(a) if i not in set(r)]则是2.39微秒,因此慢了50%。(其中仅有290纳秒是set(r)调用的时间,其余时间是哈希和查找哈希表所需的时间比遍历一个2元素列表所需的时间更长。) - abarnert
@abarnert:if表达式对于每个元素都会执行,因此您在每个循环中都创建了一个新的集合。 - Martijn Pieters
1
@abarnert:如果要删除的项目序列长度未知,我仍然会选择使用集合;另一种选择是收集有关预计要删除的项目数量的统计信息。 - Martijn Pieters
1
@abarnert,有很多故事是人们没有使用像这样简单的优化技巧。对于小规模的情况来说并无妨碍,而如果系统需要后续扩展则可以避免麻烦。特别是当原始开发人员已经离开且它位于一些深层但频繁使用的函数中时。 - John La Rooy
@gnibbler:这总是一个权衡。如果你知道你正在使用一个短的、静态的列表,那么为了让它稍微慢一点而使你的代码稍微复杂化并不是一个胜利。如果你不知道你在使用什么,那么在琐碎的情况下稍微复杂化和稍微减慢速度,但在其他情况下更快,这是一个胜利。像往常一样,在选择最佳编写方式之前,你必须知道你要编写什么。 - abarnert
显示剩余4条评论

8
明显的方法行不通:
list=["asdf","ghjk","qwer","tyui"]
removelist=[1,3] 
for index in removelist:
    del list[index]

问题在于,当您删除#1“ghjk”后,其后的所有内容都会向前移动。因此,#3不再是“tyui”,而是超出了列表的末尾。
您可以通过确保向后遍历来解决这个问题:
list=["asdf","ghjk","qwer","tyui"]
removelist=[1,3] 
for index in sorted(removelist, reverse=True):
    del list[index]

然而,通常最好只是构建一个新的过滤列表,如Martijn Pieters所建议的那样。
list = [v for i, v in enumerate(list) if i not in removelist]

وˆ‘用reversedن»£و›؟ن؛†sorted(...,reverse=True),ه¹¶ن¸”èژ·ه¾—ن؛†ه¾ˆه¤§çڑ„وˆگهٹںم€‚ - SethMMorton
@SethMMorton:只有在你知道removelist保证按正向顺序排序时,这才有效。(如果你之所以知道这一点是因为你将列表明确地写成文字,那么你最好一开始就将其写成反向顺序。) - abarnert
我明白你的意思。我没有仔细看你的算法。是的,在这种方法中,你确实需要进行排序。 - SethMMorton
我知道这很旧了,但你能解释一下它是如何工作的吗?v for i是我尚未遇到的结构,然后所有这些都被包含在一个列表中似乎很有趣,但可以理解;然后我可以理解if i not in removelist以帮助轻松制作新列表而不包括那些元素。但我不太确定这里正在发生什么。 - Andrew

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