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)]
或者,通过将赋值给切片somelist [:]
,您可以更改现有列表以仅包含您想要的项:
somelist[:] = [x for x in somelist if not determine(x)]
如果有其他引用需要反映更改,这种方法可能很有用。
除了理解之外,您还可以使用itertools
。在Python 2中:
from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)
或在Python 3中:
from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)
somelist[:] = (x for x in somelist if determine(x))
,这将创建一个生成器,避免不必要的副本。 - Rostislav Kondratenkolist_ass_slice()
函数实现了somelist[:]=
调用,内部使用了PySequence_Fast()
。这个函数总是返回一个列表,即@Alex Martelli的解决方案已经使用了列表而不是生成器,很可能更有效。 - jfssomelist
不都会被改变吗? - Bowen Liusomelist[:] = [tup for tup in somelist if determine(tup)]
b
中的内容替换字典 a
中的内容,请使用 a.clear(); a.update(b)
。 - Sven Marnachx = ['foo','bar','baz']; y = x; x = [item for item in x if determine(item)];
这段代码重新将x
赋值为列表推导式的结果,但是y
仍然引用原始列表['foo','bar','baz']
。如果你期望x
和y
引用同一个列表,可能会引入错误。要避免这种情况,可以像Alex展示的那样,对整个列表进行切片赋值,我在这里展示:x = ["foo","bar","baz"]; y = x; x[:] = [item for item in x if determine(item)];
。这会直接修改列表,确保所有引用该列表的变量(包括这里的x
和y
)都引用新列表。 - Steven T. Snyderfor x in l: print x
,然后在for
循环块的下面和下面执行l[:] = [i for i in l if i!= 3]
以删除3。 - ShadowRangerfor 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
[]
list(somelist)
将可迭代对象转换为列表。 somelist[:]
将支持切片的对象复制一份。所以它们不一定做相同的事情。在这种情况下,我想复制somelist
对象,因此我使用[:]
。 - Lennart Regebroremove()
必须遍历整个列表,因此这将需要很长时间。 - vitiralfor i in range(len(somelist) - 1, -1, -1):
if some_condition(somelist, i):
del somelist[i]
你需要倒着循环,否则就像砍断你坐在上面的树枝一样危险 :-)
Python 2用户:将range
替换为xrange
以避免创建硬编码列表。
reversed()
内置函数更加清晰地实现这一点。 - ncoghlanm
倍。 - Navinreversed(xrange(len(somelist)))
,在Python3中使用reversed(range(len(somelist)))
。 - Barmaley解决方法概述
要么:
使用链表实现/自己动手实现。
链表是支持高效项删除的合适数据结构,不会强制你进行空间/时间权衡。
CPython list
使用动态数组作为此处提到的实现方式,这不是一个很好的数据类型来支持删除操作。
标准库中似乎没有链表:
从头开始创建一个新的list()
,并在末尾.append()
,如https://dev59.com/p3M_5IYBdhLWcg3w1G6N#1207460所述。
这种方法时间有效,但空间效率较低,因为在迭代期间需要保留额外的数组副本。
按索引使用del
,如https://dev59.com/p3M_5IYBdhLWcg3w1G6N#1207485所述。
这种方法更加空间有效,因为它不需要数组副本,但它的时间效率较低,因为从动态数组中删除需要将所有后续项向后移动一位,其复杂度为O(N)。
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()
必须迭代整个列表才能找到值。std::vector::erase
,它返回被移除元素之后的有效迭代器也许背后的理由是Python列表被认为是支持动态数组的,因此任何类型的删除都会效率低下。而Java具有更好的接口层次结构,包括ArrayList
和LinkedList
实现ListIterator
。
在Python标准库中似乎也没有明确的链表类型:Python Linked List
对于这样的例子,您最好的方法是使用列表推导式
somelist = [tup for tup in somelist if determine(tup)]
在进行比调用 determine
函数更复杂的操作时,我喜欢构造一个新列表并逐步添加元素。例如:
newlist = []
for tup in somelist:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
somelist = newlist
使用remove
复制列表可能会使您的代码看起来更加整洁,如下面的一个答案所述。但是,对于非常大的列表,您绝对不应该这样做,因为这首先涉及复制整个列表,并且对于每个要删除的元素执行O(n)
的remove
操作,使其成为一个O(n^2)
算法。
for tup in somelist[:]:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
对于喜欢函数式编程的人:
somelist[:] = filter(lambda tup: not determine(tup), somelist)
或者from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
filter
一样函数式,但更符合 Python 的风格。lambda
来使用 map
或 filter
,那么列表推导式或生成器表达式 总是 更好的选择;当转换/谓词函数是由 C 实现的 Python 内置函数并且可迭代对象不是非常小的时候,map
和 filter
可能会稍微快一些,但当你需要一个 lambda
时,列表推导式或生成器表达式可以避免这种情况,因此总是更快。我需要处理一个非常长的列表,复制这个列表似乎太耗费资源了,特别是考虑到在我的情况下需要删除的项目相对于保留项目数量较少。因此,我采用了这种底层的方法。
array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
if someTest(array[i]):
del array[i]
arraySize -= 1
else:
i += 1
我不清楚删除一些元素的效率与复制一个大列表的效率相比如何。如果您有任何见解,请评论。
list
的选择,因为从列表中间删除需要线性时间。如果您不真正需要对第k个连续项进行随机访问,也许可以考虑使用OrderedDict
? - maxnewlist = []
,然后在del array[i]
之前添加newlist.append(array[i])
? - maxlist()
是一个链表,那么随机访问是昂贵的;如果list()
是一个数组,删除操作是昂贵的,因为它们需要将所有后续元素向前移动。一个不错的迭代器可以使链表实现变得更好。然而,这可能是空间高效的。 - Ciro Santilli OurBigBook.com这里大多数答案都希望你创建列表的副本。但是我有一个使用情况,列表相当长(110K项),保持缩小列表是更明智的选择。
首先,您需要用while循环替换foreach循环,
i = 0
while i < len(somelist):
if determine(somelist[i]):
del somelist[i]
else:
i += 1
<\p>i
的值在if块中并没有改变,因为您需要从相同的索引获取新项目的值,一旦旧项目被删除。如果当前列表项符合所需条件,创建一个新列表可能是明智的选择。
因此:
for item in originalList:
if (item != badValue):
newList.append(item)
为了避免必须使用新的列表名称重新编写整个项目:
originalList[:] = newList
注意,以下内容来自Python文档:
copy.copy(x) 返回x的浅复制。
copy.deepcopy(x) 返回x的深度复制。