如何在遍历列表时删除项目?
我正在用Python遍历一个包含元组的列表,想要根据一些条件把符合条件的元组删除。
for tup in somelist:
if determine(tup):
code_to_remove_tup
我应该用什么来替代 code_to_remove_tup
呢?我搞不清楚怎么用这种方式来删除这个项目。
25 个回答
399
你需要先复制一份列表,然后再对它进行遍历,否则遍历可能会失败,结果可能会让你感到意外。
举个例子(这取决于列表的类型):
for tup in somelist[:]:
etc....
一个例子:
>>> somelist = range(10)
>>> for x in somelist:
... somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]
>>> somelist = range(10)
>>> for x in somelist[:]:
... somelist.remove(x)
>>> somelist
[]
701
有些回答提到的列表推导式,说得差不多正确——但它们会创建一个全新的列表,然后把这个新列表命名为旧列表的名字,实际上并没有直接修改旧列表。这和你通过选择性删除的方式(比如Lennart的建议)是不同的。虽然这种方法更快,但如果你的列表是通过多个引用来访问的,单纯地改变一个引用而不改变列表对象本身,可能会导致一些微妙而严重的错误。
幸运的是,我们可以很简单地同时获得列表推导式的速度和在原地修改的效果——只需要这样写:
somelist[:] = [tup for tup in somelist if determine(tup)]
注意这个答案和其他答案的细微区别:这个答案并没有给一个变量名赋值,而是给一个列表切片赋值,而这个切片恰好是整个列表。这样做是直接在同一个Python列表对象内替换列表的内容,而不是像其他答案那样只是改变了一个引用(从旧列表对象指向新列表对象)。
1104
你可以使用列表推导式来创建一个新列表,这个列表只包含你不想删除的元素:
somelist = [x for x in somelist if not determine(x)]
或者,通过给切片somelist[:]
赋值,你可以直接修改现有的列表,只保留你想要的项目:
somelist[:] = [x for x in somelist if not determine(x)]
这种方法在有其他地方引用somelist
时会很有用,因为这样修改会反映到所有引用的地方。
除了列表推导式,你还可以使用itertools
。在Python 2中:
from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)
或者在Python 3中:
from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)