I was wondering, if there is way in Python to modify collections without creating new ones. E.g.:
我想知道,在Python中是否有办法在不创建新集合的情况下修改集合。例如:
lst = [1, 2, 3, 4, 5, 6]
new_lst = [i for i in lst if i > 3]
Works just fine, but a new collection is created. Is there a reason, that Python collections lack a filter()
method (or similar) that would modify the collection object in place?
工作正常,但是创建了一个新的集合。有什么原因,Python集合缺少一个过滤器()方法(或类似的)来修改集合对象?
6 个解决方案
#1
7
The other answers are correct; if you want all the names pointing to the old list to point to the new list you can use slice assignment.
其他答案是正确的;如果希望所有指向旧列表的名称指向新列表,可以使用slice赋值。
However, that's not truly in-place creation; the new list is first created elsewhere. The link in Sven's answer is good.
然而,这并不是真正的就地创造;新列表首先在其他地方创建。斯文回答的链接是好的。
The reason there isn't one that truly operates in-place is that while making a new list like that is O(n), each truly in-place item removal would be O(k) by itself, where k
is the length of the list from the removal point on. The only way to avoid that with Python lists is to use some temporary storage, which is what you're doing by using slice assignment.
没有一个真正在原地运行的原因是,虽然创建一个新的列表是O(n),但是每个真正在原地的项目删除都是O(k)本身,其中k是列表从上的删除点开始的长度。使用Python列表避免这种情况的唯一方法是使用一些临时存储,这是通过使用片分配来实现的。
An example of an in-place O(n) filter on a collections.deque
, in case you don't need to store your data in a list
:
deque,如果你不需要将你的数据存储在一个列表中:
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
18
If you want to do this in place, just use
如果你想在合适的地方做这个,就用它
lst[:] = [i for i in lst if i > 3]
This won't be faster or save any memory, but it changes the object in place, if this is the semantics you need.
这将不会更快,也不会保存任何内存,但如果这是您需要的语义,那么它将更改相应的对象。
#3
1
The lst[:]
solution by @Sven Marnach is one option. You can also perform this operation in-place, using constant extra memory, with
@Sven Marnach的lst[:]解决方案是一个选项。您也可以使用固定的额外内存来执行这个操作。
>>> i = 0
>>> while i < len(lst):
... if lst[i] <= 3:
... del lst[i]
... else:
... i += 1
...
>>> lst
[4, 5, 6]
... but this solution is not very readable and takes quadratic time due to all the element shifting involved.
…但是这个解不是很容易读懂,并且由于涉及的所有元素移位,需要二次时间。
#4
1
Correcting @larsmans original solution, you could either do
纠正@larsmans的原始解决方案,你可以这么做
i = 0
while i < len(lst):
if lst[i] <= 3:
del lst[i]
else
i += 1
or
或
i = len(lst)
while i > 0:
if lst[i-1] <= 3:
del lst[i-1]
i -= 1
Reason is the "index shift" which happens with the del
. If I del
at a ceratin index, I have to re-examine that index because it now holds a different value.
原因是“指数转移”,它与德尔一起发生。如果我在ceratin索引中,我必须重新检查那个索引,因为它现在持有不同的值。
#6
0
I think it's in place transformation;
我认为这是适当的转变;
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
result
结果
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]
#1
7
The other answers are correct; if you want all the names pointing to the old list to point to the new list you can use slice assignment.
其他答案是正确的;如果希望所有指向旧列表的名称指向新列表,可以使用slice赋值。
However, that's not truly in-place creation; the new list is first created elsewhere. The link in Sven's answer is good.
然而,这并不是真正的就地创造;新列表首先在其他地方创建。斯文回答的链接是好的。
The reason there isn't one that truly operates in-place is that while making a new list like that is O(n), each truly in-place item removal would be O(k) by itself, where k
is the length of the list from the removal point on. The only way to avoid that with Python lists is to use some temporary storage, which is what you're doing by using slice assignment.
没有一个真正在原地运行的原因是,虽然创建一个新的列表是O(n),但是每个真正在原地的项目删除都是O(k)本身,其中k是列表从上的删除点开始的长度。使用Python列表避免这种情况的唯一方法是使用一些临时存储,这是通过使用片分配来实现的。
An example of an in-place O(n) filter on a collections.deque
, in case you don't need to store your data in a list
:
deque,如果你不需要将你的数据存储在一个列表中:
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
18
If you want to do this in place, just use
如果你想在合适的地方做这个,就用它
lst[:] = [i for i in lst if i > 3]
This won't be faster or save any memory, but it changes the object in place, if this is the semantics you need.
这将不会更快,也不会保存任何内存,但如果这是您需要的语义,那么它将更改相应的对象。
#3
1
The lst[:]
solution by @Sven Marnach is one option. You can also perform this operation in-place, using constant extra memory, with
@Sven Marnach的lst[:]解决方案是一个选项。您也可以使用固定的额外内存来执行这个操作。
>>> i = 0
>>> while i < len(lst):
... if lst[i] <= 3:
... del lst[i]
... else:
... i += 1
...
>>> lst
[4, 5, 6]
... but this solution is not very readable and takes quadratic time due to all the element shifting involved.
…但是这个解不是很容易读懂,并且由于涉及的所有元素移位,需要二次时间。
#4
1
Correcting @larsmans original solution, you could either do
纠正@larsmans的原始解决方案,你可以这么做
i = 0
while i < len(lst):
if lst[i] <= 3:
del lst[i]
else
i += 1
or
或
i = len(lst)
while i > 0:
if lst[i-1] <= 3:
del lst[i-1]
i -= 1
Reason is the "index shift" which happens with the del
. If I del
at a ceratin index, I have to re-examine that index because it now holds a different value.
原因是“指数转移”,它与德尔一起发生。如果我在ceratin索引中,我必须重新检查那个索引,因为它现在持有不同的值。
#5
#6
0
I think it's in place transformation;
我认为这是适当的转变;
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
result
结果
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]