2011-02-10 2 views
5

Я просматриваю список и удаляю элементы, которые удовлетворяют моему состоянию. Но почему это не работает, как указано ниже? Спасибо.Как безопасно удалить элементы из списка в Python

>>> a=[ i for i in range(4)] 
>>> a 
[0, 1, 2, 3] 
>>> for e in a: 
...  if (e > 1) and (e < 4): 
...   a.remove(e) 
... 
>>> a 
[0, 1, 3] 
>>> a=[ i for i in range(4)] 
>>> for e in a: 
...  if (e > -1) and (e < 3): 
...   a.remove(e) 
... 
>>> a 
[1, 3] 

ответ

9

Вы не можете изменить что-то во время его итерации. Результаты являются странными и контр-интуитивными, и почти никогда не то, что вы хотите. Фактически, многие коллекции явно запрещают это (например, sets и dicts).

Вместо этого переберите копию (for e in a[:]: ...) или вместо изменения существующего списка отфильтруйте его, чтобы получить новый список, содержащий нужные элементы ([e for e in a if ...]). Обратите внимание, что во многих случаях вам не нужно снова итерации фильтровать, просто объединить фильтрацию с генерацией данных.

5

Почему бы вам просто не сделать это изначально в понимании списка? Например.

[i for i in range(4) if i <= 1 or i >= 4] 

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

[x for x in a if x <= 1 or x >= 4] 
+0

Это возвращает элементы, которые на самом деле должны быть удалены. –

+0

@Sven извините, я исправлю это –

1

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

В вашем случае вы можете использовать функцию лямбда так:

a = filter(lambda x: x > 1 and x < 4, range(4)) 

или если у вас есть список уже:

a = range(4) 
a = filter(lambda x: x > 1 and x < 4, a) 

помнить, что если вы используете python3 он возвращает итератор, а не список.

+0

Для простых случаев, подобных этому, я бы предпочел понимание списка над 'filter'. –

2

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

В этом случае ответом является запоминание индексов списка элементов для удаления, а затем итерация по списку индексов, отсортированных от наибольшего до наименьшего, удаление элементов.

+0

Скажите, что у вас есть список из 1 миллиона элементов и 4 удаляются. Фильтрация означает перетасовку около 1 000 000 элементов, в то время как ваше предложение будет включать перетасовку в среднем вдвое больше. Конечно, другие факторы будут означать, что это не так просто, но если вы на самом деле не приурочили код, который я бы сказал, придерживайтесь простейшей (фильтрации), так как вы ничего не выиграете, сделав больше сложный. – Duncan

+0

Я бы не подумал о том, чтобы найти элементы для «перетасовки». И в конце у вас есть список из 4 элементов, которые вы повторяете в обратном порядке, а в удалениях используются несогласованные элементы списка, так что опять же, где находится тасовка? –

2

Самый простой способ визуализировать это - подумать об итерации, работающей над списками-смещениями вместо фактических элементов - сделать что-то к первому элементу, затем второму элементу, затем третьему элементу, пока не закончится элемент , Если изменить количество элементов в списке, он изменяет смещения всех остальных элементов в списке:

lst = [1,2,3,4] 
for item in lst: 
    if item==2: 
     lst.remove(item) 
    else: 
     print item 
print lst 

результатов в

1 
4 
[1,3,4] 

, который имеет смысл, если вы шагаете через него, как так :

[1,2,3,4] 
^ 
first item is not 2, so print it -> 1 

[1,2,3,4] 
^
    second item is 2, so remove it 

[1,3,4] 
    ^
    third item is 4, so print it -> 4 

Единственное реальное решение не изменить количество элементов в списке, пока вы итерацию над ней. Скопируйте элементы, которые хотите сохранить в новый список, или отследите значения, которые вы хотите удалить, и сделайте удаление по значению в отдельном проходе.

+0

+1 для объяснения неприятных деталей. – delnan

Смежные вопросы