Как удалить элементы списка при зацикливании самого списка, не дублируя его - PullRequest
1 голос
/ 10 февраля 2010

Я потерял немного времени в этом Python для утверждения:

class MyListContainer:
    def __init__(self):
        self.list = []

    def purge(self):
        for object in self.list:
            if (object.my_cond()):
                self.list.remove(object)
        return self.list

container = MyListContainer()

# now suppose both obj.my_cond() return True
obj1 = MyCustomObject(par)
obj2 = MyCustomObject(other_par)

container.list = [obj1, obj2]

# returning not an empty list but [obj2]
container.purge()

Это не работает так, как я ожидал, потому что, когда цикл «очистки» удаляет первый объект в списке, второй перемещается в начало списка, и цикл заканчивается.

Я решил дублировать self.list перед циклом for:

...
local_list = self.list[:]
for object in local_list:
...

Полагаю, что оператор for перестает работать, потому что я изменяю длину исходного списка. Может кто-нибудь прояснить этот момент?

И есть ли более "элегантный" способ решения этой проблемы? Если у меня есть несколько элементов внутри списка, дублирование его каждый раз не кажется хорошей идеей.

Возможно, функция filter () верна, но я хочу использовать другой подход, если таковой имеется.

Я новичок.


Подводя итог вашим полезным ответам:

  • Никогда не редактируйте зацикленный список
  • Дублирование списка или использование списочных значений
  • Дублирование списка не может тратить вашу память или, в этом случае, кто думает об этом

Ответы [ 7 ]

4 голосов
/ 10 февраля 2010

Не пытайся. Просто не надо. Сделайте копию или создайте новый список.

3 голосов
/ 10 февраля 2010

Просто составьте себе новый список:

def purge(self):
    self.list = [object for object in self.list if not object.my_cond()]
    return self.list

Зарезервируйте любую оптимизацию, пока вы не профилируете и не обнаружите, что этот метод действительно является узким местом вашего приложения. (Держу пари, этого не будет.)

2 голосов
/ 10 февраля 2010

Фильтр (или список понимания) это путь. Если вы хотите сделать это на месте, что-то вроде этого будет работать:

purge = []
for i,object in enumerate(self.list):
    if object.mycond()
        purge.append(i)
for i in reversed(purge):
    del self.list[i]

Или, альтернативно, список очистки может быть составлен с пониманием, а сокращенная версия выглядит следующим образом:

for i in reversed([ i for (i,o) in enumerate(self.list) if o.mycond() ]):
    del self.list[i]
2 голосов
/ 10 февраля 2010

В Python переменные на самом деле являются метками для данных. Дублирование списка - это, по большей части, создание нового набора указателей на данные из первого списка. Не переживай из-за этого.

Понимание списка - ваш друг.

, например

>>> a = range(20)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> [ x for x in a if x % 2 == 0 ]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
0 голосов
/ 10 февраля 2010
indeces = []
minus = 0

for i in range(self.list):
    if cond(self.list[i]):
        indeces.append(i)

for i in indeces:
    self.list = self.list[:(i-minus)].extend(self.list[i-minus+1:])
0 голосов
/ 10 февраля 2010

Сократить список на месте совершенно безопасно, если вы сделаете это в обратном порядке!

>>> a=range(20)
>>> for i in reversed(range(len(a))):
...     if a[i]%2: del a[i]
... 
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Другой способ - переназначить весь фрагмент

>>> a=range(20)
>>> a[:]=(x for x in a if not x%2)
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Если элементы в списке уникальны, это тоже работает

>>> a=range(20)
>>> for item in reversed(a):
...  if item%2: a.remove(item)
... 
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Вот еще одно объяснение в ответ на комментарий Юрия

Предположим, у нас есть

>>> a=[0,1,2,3,4,5]

Теперь наивно пытаемся удалить 3-й и 4-й элементы

>>> del a[3]
>>> del a[4]
>>> a
[0, 1, 2, 4] # didn't work because the position of all the item with index >=3 was changed

Однако, если мы сделаем del в обратном порядке

>>> a=[0,1,2,3,4,5]
>>> del a[4]
>>> del a[3]
>>> a
[0, 1, 2, 5] # this is the desired result

Теперь распространите эту идею на цикл for с условием удаления, и вы увидите, что удаление из активного списка возможно

0 голосов
/ 10 февраля 2010

Ваше второе решение, в котором вы дублируете список, - правильный путь. После этого вы можете просто заменить старый список дубликатом, если это необходимо.

...