如何通过就地筛选修改python集合?

如何通过就地筛选修改python集合?,python,collections,Python,Collections,我想知道,在Python中是否有办法在不创建新集合的情况下修改集合。例如: lst = [1, 2, 3, 4, 5, 6] new_lst = [i for i in lst if i > 3] 工作正常,但会创建一个新集合。Python集合缺少一个filter()方法(或类似方法)来就地修改集合对象,这是有原因的吗?如果您想就地修改集合对象,只需使用 lst[:] = [i for i in lst if i > 3] 如果这是您需要的语义,它会在适当的位置更改对象。,因为它

我想知道,在Python中是否有办法在不创建新集合的情况下修改集合。例如:

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

工作正常,但会创建一个新集合。Python集合缺少一个
filter()
方法(或类似方法)来就地修改集合对象,这是有原因的吗?

如果您想就地修改集合对象,只需使用

lst[:] = [i for i in lst if i > 3]
如果这是您需要的语义,它会在适当的位置更改对象。

,因为它是


@Sven Marnach提供的
lst[:]
解决方案是一种选择。您还可以使用恒定的额外内存,使用

>>> i = 0
>>> while i < len(lst):
...  if lst[i] <= 3:
...   del lst[i]
...  else:
...   i += 1
... 
>>> lst
[4, 5, 6]
>>i=0
>>>而i>lst
[4, 5, 6]

。。。但由于涉及到所有元素的移动,该解决方案的可读性不强,并且需要二次时间。

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

然而,这并不是真正的就地创造;新列表首先在别处创建。斯文回答中的链接很好

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

如果您不需要将数据存储在
列表中,则
collections.deque
上的就地O(n)过滤器示例如下:

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])
更正,你可以这样做

    i = 0
    while i < len(lst):
        if lst[i] <= 3:
            del lst[i]
        else:
            i += 1
i=0
而i如果lst[i-1]我认为它是就地转换

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]


那么这段代码的意义是什么呢
lst=[…]
也有同样的效果,不是吗?如果您在函数内部,并且由于某种原因不想返回新列表,则需要对其进行适当的修改,以便在外部可以进行更改。@BasicWolf:主要区别在于,这不会分配新列表,因此如果在客户端之间共享该列表,奇怪的是,您在另一个引用的答案中解释说,赋值指令的正确成员首先被求值(因此在内存中的其他位置创建了一个新对象),并且在这个答案中,您写道该指令更改了列表。在我看来,这不是纯粹的原地踏步。说
id(lst)
在赋值后比以前保持不变是更正确的描述,但无论如何与问题不符,alas@eyquem我将其描述为使用O(n)额外存储的就地存储。您所描述的“纯就地”,就像我的
deque
示例一样,我会使用O(1)额外存储调用就地。我认为“就地”适用于两者;有时,当人们要求在适当的位置使用时,他们只需要对现有对象进行变异,有时他们试图最小化内存使用。但每个
del lst[i]
都需要线性时间,这就是为什么默认情况下不存在这种情况。@agf:我第二次写常量时,我指的是二次。更新,谢谢。当我尝试使用原始列表时,我的结果列表将变成
[2,4,5,6]
。写一个自己的答案。我花了10分钟才明白这个答案是在GLGL的评论之后编辑的,没有发出任何信号:(Thx提示-我现在指的是他的文章的旧版本,尊重他的编辑。非常感谢你,agf(和其他人:)我真的很想知道原因。通过在列表中使用读写指针,可以实现一个需要线性时间的低级就地筛选函数。顺便说一句,在链表的情况下,整个列表的筛选将使用O(n)。我想你的意思是O(k)对于类向量结构?@BasicWolf,Python的列表是类向量结构,因此关于O(k)的语句对于Python列表和我们不需要的简单删除每个元素的方法来说都是如此。Python中没有本机的链表结构;如果需要,显然可以对它们进行编码,但使用内置类型几乎总是更可取的。@agf:切片分配是一条单字节代码指令。当使用带线程的CPython时,它将是如果你真的,绝对必须修改它,为什么不检查每个值,然后弹出(i)你不喜欢的值呢?关于“缺乏”其他方法,这是因为在Python中“应该有一种——最好只有一种——显而易见的方法来做。”下面答案中的列表切片操作是进行就地修改的首选方法。它应该是自然正交的,因为lst[index]访问单个元素lst[start:stop]访问元素的范围/片段。BasicWalf事实上你是对的:据我所知,没有方法或函数可以处理就地转换和问题“为什么?”是有效的。可以编写执行此类转换的代码段这一事实并不是一个合理的理由,否则,我们可以编写自己的代码段来执行序列反转,以证明不会将reverse()作为内置函数。但是有reversed()内置功能…我支持投票,因为你的问题看起来像是刺激性的反思。@eyquem我认为原因是straigthforward。因为他们的方法不是一种有效的方法来过滤Python
列表,所以没有提供任何方法来做这件事——这会鼓励人们在最好不要做的时候尝试并在适当的地方做然而,re是一种非常有效的反向迭代方法,因此提供了一个解决方案。尽管如此,仍然存在许多无序集合。例如,过滤
集合
dict
。虽然这修正了larsman的解,但并没有改善它——它仍然是二次时间。嗯,这取决于定义。它并没有改善其p表演
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]