Как изменить коллекции Python путем фильтрации на месте? - PullRequest
11 голосов
/ 07 ноября 2011

Мне было интересно, есть ли способ в Python изменять коллекции без создания новых. E.g.:

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

Работает просто отлично, но создается новая коллекция. Есть ли причина, по которой в коллекциях Python отсутствует метод filter() (или аналогичный), который бы изменял объект коллекции на месте?

Ответы [ 6 ]

20 голосов
/ 07 ноября 2011

Если вы хотите сделать это на месте, просто используйте

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

Этот не будет быстрее или экономит память , но он меняет объект на месте, если вам нужна семантика.

9 голосов
/ 07 ноября 2011

Остальные ответы верны; если вы хотите, чтобы все имена, указывающие на старый список, указывали на новый список, вы можете использовать назначение срезов.

Однако, это не совсем творение на месте; Новый список сначала создается в другом месте. Ссылка в ответе Свена хорошая.

Причина , которая действительно работает на месте, заключается в том, что при создании нового списка, подобного этому, будет O (n), каждое действительно удаленное удаление элементов будет O (k) само по себе , где k - длина списка от точки удаления до. Единственный способ избежать этого в списках Python - это использовать какое-то временное хранилище, что вы и делаете, используя назначение слайсов.

Пример встроенного O (n) -фильтра для collections.deque, если вам не нужно сохранять данные в 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 голосов
/ 07 ноября 2011

Исправляя @ оригинальное решение larsmans , вы можете сделать

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.Если я del по кератиновому индексу, мне придется пересмотреть этот индекс, потому что теперь он имеет другое значение.

1 голос
/ 07 ноября 2011

Решение lst[:] от @Sven Marnach является одним из вариантов. Вы также можете выполнить эту операцию на месте, используя постоянную дополнительную память, с помощью

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

... но это решение не очень читабельно и требует квадратичного времени из-за всех задействованных смещений элементов.

0 голосов
/ 07 ноября 2011

Я думаю, что это преобразование на месте;

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]
0 голосов
/ 07 ноября 2011

Потому что не нужно .

lst[:] = [i for i in lst if i > 3]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...