如何通过就地过滤来修改python集合?

时间:2011-11-07 13:59:37

标签: python collections

我想知道,如果在Python中有办法修改集合而不创建新集合。 E.g:

lst = [1, 2, 3, 4, 5, 6]
new_lst = [i for i in lst if i > 3]

工作正常,但会创建一个新的集合。有没有理由说Python集合缺少一个filter()方法(或类似方法)来修改集合对象?

6 个答案:

答案 0 :(得分:20)

如果您想这样做,请使用

lst[:] = [i for i in lst if i > 3]

这个won't be faster or save any memory,但它会改变对象,如果这是你需要的语义。

答案 1 :(得分:8)

其他答案是正确的;如果您希望指向旧列表的所有名称都指向新列表,则可以使用切片分配。

但是,这不是真正的就地创造;新列表首先在别处创建。 Sven答案中的链接很好。

原因没有一个真正在原地运作的原因是,当创建一个像这样的新列表是O(n)时,每个真正的就地项目删除都是O(k )本身,其中k是从删除点开始的列表长度。使用Python列表避免这种情况的唯一方法是使用一些临时存储,这是您使用切片分配所做的。

collections.deque上的就地O(n)过滤器示例,以防您不需要将数据存储在list中:

from collections import deque

def dequefilter(deck, condition):
    for _ in xrange(len(deck)):
        item = deck.popleft()
        if condition(item):
            deck.append(item)

deck = deque((1, 2, 3, 4, 5))
dequefilter(deck, lambda x: x > 2) # or operator.gt(2)
print deck
# deque([3, 4, 5])

答案 2 :(得分:2)

更正@larsmans original solution,您可以

i = 0
while i < len(lst):
    if lst[i] <= 3:
        del lst[i]
    else
        i += 1

i = len(lst)
while i > 0:
    if lst[i-1] <= 3:
        del lst[i-1]
    i -= 1

原因是del发生的“索引转换”。如果我在一个ceratin索引中del,我必须重新检查该索引,因为它现在拥有不同的值。

答案 3 :(得分:1)

@Sven Marnach的lst[:]解决方案是一种选择。您也可以使用常量额外内存使用

就地执行此操作
>>> i = 0
>>> while i < len(lst):
...  if lst[i] <= 3:
...   del lst[i]
...  else:
...   i += 1
... 
>>> lst
[4, 5, 6]

...但是这个解决方案不是非常易读,并且由于所涉及的所有元素转移都需要二次时间。

答案 4 :(得分:0)

因为它是not needed

lst[:] = [i for i in lst if i > 3]

答案 5 :(得分:0)

我认为这已经到位了;

lst = [1,2,3,4,5,6,7,8,9,10,11]
to_exclude = [8,4,11,9]
print 'lst == %s\nto_exclude == %s' % (lst,to_exclude)

for i in xrange(len(lst)-1,-1,-1):
    if lst[i] in to_exclude:
        lst.pop(i)

print '\nlst ==',lst

结果

lst == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
to_exclude == [8, 4, 11, 9]

lst == [1, 2, 3, 5, 6, 7, 10]