我在Python中迭代一个元组列表,并试图删除它们,如果它们满足某些条件。
for tup in somelist:
if determine(tup):
code_to_remove_tup
我应该用什么来代替code_to_remove_tup?我不知道怎么用这种方式把东西拿掉。
我在Python中迭代一个元组列表,并试图删除它们,如果它们满足某些条件。
for tup in somelist:
if determine(tup):
code_to_remove_tup
我应该用什么来代替code_to_remove_tup?我不知道怎么用这种方式把东西拿掉。
当前回答
您可以使用列表推导式创建一个新列表,其中只包含您不想删除的元素:
somelist = [x for x in somelist if not determine(x)]
或者,通过分配slice somlist[:],你可以改变现有的列表,只包含你想要的项:
somelist[:] = [x for x in somelist if not determine(x)]
如果对某个列表的其他引用需要反映更改,则此方法可能很有用。
你也可以使用itertools来代替理解函数。在Python 2中:
from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)
或者在python3中:
from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)
其他回答
我需要用一个巨大的列表来做这件事,复制列表似乎很昂贵,特别是因为在我的情况下,删除的数量与保留的项目相比很少。我采用了这种低层次的方法。
array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
if someTest(array[i]):
del array[i]
arraySize -= 1
else:
i += 1
我不知道的是,与复制一个大列表相比,几个删除操作的效率有多高。如果你有什么见解,请评论。
如果您想在迭代期间做其他事情,那么最好同时获得索引(这保证您能够引用它,例如,如果您有一个字典列表)和实际的列表项内容。
inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]
for idx, i in enumerate(inlist):
do some stuff with i['field1']
if somecondition:
xlist.append(idx)
for i in reversed(xlist): del inlist[i]
Enumerate使您可以同时访问项和索引。反向是为了以后你要删除的索引不会改变。
你可以反过来尝试for- loops,这样对于some_list,你就可以这样做:
list_len = len(some_list)
for i in range(list_len):
reverse_i = list_len - 1 - i
cur = some_list[reverse_i]
# some logic with cur element
if some_condition:
some_list.pop(reverse_i)
这样索引是对齐的,并且不会受到列表更新的影响(无论是否弹出cur元素)。
对于喜欢函数式编程的人:
somelist[:] = filter(lambda tup: not determine(tup), somelist)
or
from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
变通方案概述
:
use a linked list implementation/roll your own. A linked list is the proper data structure to support efficient item removal, and does not force you to make space/time tradeoffs. A CPython list is implemented with dynamic arrays as mentioned here, which is not a good data type to support removals. There doesn't seem to be a linked list in the standard library however: Is there a linked list predefined library in Python? https://github.com/ajakubek/python-llist start a new list() from scratch, and .append() back at the end as mentioned at: https://stackoverflow.com/a/1207460/895245 This time efficient, but less space efficient because it keeps an extra copy of the array around during iteration. use del with an index as mentioned at: https://stackoverflow.com/a/1207485/895245 This is more space efficient since it dispenses the array copy, but it is less time efficient, because removal from dynamic arrays requires shifting all following items back by one, which is O(N).
一般来说,如果你做得很快,不想添加一个自定义LinkedList类,你只需要在默认情况下使用更快的.append()选项,除非内存是一个大问题。
官方Python 2教程4.2。“声明”
https://docs.python.org/2/tutorial/controlflow.html#for-statements
这部分文档明确说明:
您需要复制迭代列表才能修改它 一种方法是使用切片符号[:]
If you need to modify the sequence you are iterating over while inside the loop (for example to duplicate selected items), it is recommended that you first make a copy. Iterating over a sequence does not implicitly make a copy. The slice notation makes this especially convenient: >>> words = ['cat', 'window', 'defenestrate'] >>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words ['defenestrate', 'cat', 'window', 'defenestrate']
Python 2文档7.3。“for语句”
https://docs.python.org/2/reference/compound_stmts.html#for
这部分文档再次说明你必须复制一份,并给出了一个实际的删除示例:
Note: There is a subtlety when the sequence is being modified by the loop (this can only occur for mutable sequences, i.e. lists). An internal counter is used to keep track of which item is used next, and this is incremented on each iteration. When this counter has reached the length of the sequence the loop terminates. This means that if the suite deletes the current (or a previous) item from the sequence, the next item will be skipped (since it gets the index of the current item which has already been treated). Likewise, if the suite inserts an item in the sequence before the current item, the current item will be treated again the next time through the loop. This can lead to nasty bugs that can be avoided by making a temporary copy using a slice of the whole sequence, e.g., for x in a[:]:
if x < 0: a.remove(x)
然而,我不同意这个实现,因为.remove()必须遍历整个列表才能找到值。
Python能做得更好吗?
似乎这个特定的Python API可以得到改进。例如,将其与:
Java ListIterator::删除哪些文档“此调用只能对next或previous调用一次” c++ std::vector::erase,返回被删除元素之后的一个有效的互操作器
这两种方法都清楚地表明,除了使用迭代器本身,您不能修改正在迭代的列表,并为您提供了在不复制列表的情况下修改列表的有效方法。
可能潜在的基本原理是,Python列表被假定为动态数组支持,因此任何类型的删除都将是低效的,而Java在ListIterator的ArrayList和LinkedList实现方面都有更好的接口层次结构。
在Python标准库中似乎也没有显式的链表类型:Python链表