Как удалить элементы из списка во время итерации?

StackOverflow https://stackoverflow.com/questions/1207406

  •  05-07-2019
  •  | 
  •  

Вопрос

Я перебираю список кортежей в Python и пытаюсь удалить их, если они соответствуют определенным критериям.

for tup in somelist:
    if determine(tup):
         code_to_remove_tup

Что я должен использовать вместо code_to_remove_tup?Я не могу понять, как удалить элемент таким образом.

Это было полезно?

Решение

Вы можете использовать понимание списка, чтобы создать новый список, содержащий только те элементы, которые вы не хотите удалять:

somelist = [x for x in somelist if not determine(x)]

Или, назначив слайсу somelist [:] , вы можете изменить существующий список, чтобы он содержал только те элементы, которые вам нужны:

somelist[:] = [x for x in somelist if not determine(x)]

Этот подход может быть полезен, если есть другие ссылки на somelist , которые должны отражать изменения.

Вместо понимания вы также можете использовать itertools . В Python 2:

from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)

Или в Python 3:

from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)

Другие советы

Ответы, предполагающие понимание списка, почти верны - за исключением того, что они строят полностью новый список и затем дают ему то же имя, что и старый список, они НЕ изменяют старый список на месте. Это отличается от того, что вы будете делать выборочным удалением, как в @ предложении Леннарта - это быстрее, но если ваш Доступ к списку осуществляется с помощью нескольких ссылок. Тот факт, что вы просто перезаписываете одну из ссылок и НЕ изменяете сам объект списка, может привести к тонким, катастрофическим ошибкам.

К счастью, очень легко получить как скорость понимания списка, так и требуемую семантику изменения на месте - просто код:

somelist[:] = [tup for tup in somelist if determine(tup)]

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

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

Например (зависит от типа списка):

for tup in somelist[:]:
    etc....

Пример:

>>> somelist = range(10)
>>> for x in somelist:
...     somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]

>>> somelist = range(10)
>>> for x in somelist[:]:
...     somelist.remove(x)
>>> somelist
[]
for i in range(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

Вам нужно идти задом наперед, иначе это немного похоже на спиливание ветки дерева, на которой вы сидите: -)

Пользователи Python 2: замените range на xrange , чтобы избежать создания жестко закодированного списка

Лучшим подходом для такого примера будет понимание списка

somelist = [tup for tup in somelist if determine(tup)]

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

newlist = []
for tup in somelist:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)
somelist = newlist

Копирование списка с использованием remove может сделать ваш код немного чище, как описано в одном из ответов ниже. Вам определенно не следует делать это для очень больших списков, так как для этого необходимо сначала скопировать весь список, а также выполнить операцию O (n) remove для каждого удаляемого элемента, сделать это алгоритмом O (n ^ 2) .

for tup in somelist[:]:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)

Официальный учебник по Python 2 4.2."для заявлений"

https://docs.python.org/2/tutorial/controlflow.html#for-statements

В этой части документов ясно дается понять, что:

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

Если вам нужно изменить последовательность, которую вы повторяете внутри цикла (например, для дублирования выбранных элементов), рекомендуется сначала сделать копию.Повторение последовательности не приводит к неявному созданию копии.Обозначение среза делает это особенно удобным:

>>> words = ['cat', 'window', 'defenestrate']
>>> for w in words[:]:  # Loop over a slice copy of the entire list.
...     if len(w) > 6:
...         words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']

Документация по Python 2 7.3."Оператор for"

https://docs.python.org/2/reference/compound_stmts.html#for

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

Примечание:Существует тонкость, когда последовательность изменяется циклом (это может происходить только для изменяемых последовательностей, т.е.списки).Внутренний счетчик используется для отслеживания того, какой элемент используется следующим, и он увеличивается на каждой итерации.Когда этот счетчик достигает длины последовательности, цикл завершается.Это означает, что если набор удаляет текущий (или предыдущий) элемент из последовательности, следующий элемент будет пропущен (поскольку он получает индекс текущего элемента, который уже был обработан).Аналогично, если набор вставляет элемент в последовательность перед текущим элементом, текущий элемент будет обработан снова в следующий раз через цикл.Это может привести к неприятным ошибкам, которых можно избежать, создав временную копию, используя фрагмент всей последовательности, например,

for x in a[:]:
    if x < 0: a.remove(x)

Однако я не согласен с такой реализацией, поскольку .remove() должен повторять весь список чтобы найти значение.

Вместо этого либо:

  • создайте новый массив с нуля, и .append() вернемся в конец: https://stackoverflow.com/a/1207460/895245

    Это экономит время, но меньше места, поскольку сохраняет копию массива во время итерации.

  • использование del с индексом: https://stackoverflow.com/a/1207485/895245

    Это более экономично с точки зрения пространства, поскольку распределяет копию массива, но требует меньше времени, поскольку CPython перечисляет реализованы с помощью динамических массивов.

    Это означает, что для удаления элемента требуется сдвинуть все последующие элементы обратно на единицу, которая равна O (N).

Как правило, вы просто хотите двигаться быстрее .append() опция по умолчанию, если только память не вызывает большой озабоченности.

Мог бы Python сделать это лучше?

Похоже, что этот конкретный Python API можно было бы улучшить.Сравните его, например, с его аналогом на Java Составитель списка, что делает предельно ясным, что вы не можете изменять итерируемый список иначе, как с помощью самого итератора, и дает вам эффективные способы сделать это без копирования списка.

Возможно, основное обоснование заключается в том, что предполагается, что списки Python поддерживаются динамическим массивом, и поэтому любой тип удаления в любом случае будет неэффективным по времени, в то время как Java имеет более приятную иерархию интерфейса с обоими ArrayList и LinkedList реализации ListIterator.

Похоже, что в Python stdlib также нет явного типа связанного списка: Связанный список на Python

Для тех, кто любит функциональное программирование:

somelist[:] = filter(lambda tup: not determine(tup), somelist)

или

from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))

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

итак:

for item in originalList:
   if (item != badValue):
        newList.append(item)

и чтобы избежать необходимости перекодировать весь проект с новым именем lists:

originalList[:] = newList

примечание, взятое из документации Python:

копировать.копировать (x) Возвращает неглубокую копию x .

копировать.глубокая копия (x) Возвращает глубокую копию x .

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

array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
    if someTest(array[i]):
        del array[i]
        arraySize -= 1
    else:
        i += 1

Чего я не знаю, так это того, насколько эффективна пара удалений по сравнению с копированием большого списка. Пожалуйста, прокомментируйте, если у вас есть понимание.

Этот ответ был первоначально написан в ответ на вопрос, который с тех пор был помечен как дублирующий:Удаление координат из списка на python

В вашем коде есть две проблемы:

1) При использовании функции remove() вы пытаетесь удалить целые числа, тогда как вам нужно удалить кортеж.

2) Цикл for пропустит элементы в вашем списке.

Давайте рассмотрим, что происходит, когда мы выполняем ваш код:

>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
...   if a < 0 or b < 0:
...     L1.remove(a,b)
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)

Первая проблема заключается в том, что вы передаете оба 'a' и 'b' в функцию remove(), но функция remove() принимает только один аргумент.Итак, как мы можем заставить remove() правильно работать с вашим списком?Нам нужно выяснить, что представляет собой каждый элемент вашего списка.В этом случае каждый из них представляет собой кортеж.Чтобы убедиться в этом, давайте обратимся к одному элементу списка (индексация начинается с 0).:

>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>

Ага!Каждый элемент L1 на самом деле является кортежем.Итак, это то, что нам нужно передать в remove().Кортежи в python очень просты, они просто создаются путем заключения значений в круглые скобки."a, b" - это не кортеж, но "(a, b)" - это кортеж.Поэтому мы модифицируем ваш код и запускаем его снова:

# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))

Этот код выполняется без каких-либо ошибок, но давайте посмотрим на список, который он выводит:

L1 is now: [(1, 2), (5, 6), (1, -2)]

Почему (1, -2) все еще в вашем списке?Оказывается, изменять список при использовании цикла для перебора по нему - очень плохая идея без особой осторожности.Причина, по которой (1, -2) остается в списке, заключается в том, что местоположения каждого элемента в списке изменялись между итерациями цикла for.Давайте посмотрим, что произойдет, если мы добавим в приведенный выше код более длинный список:

L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

Как вы можете сделать вывод из этого результата, каждый раз, когда условный оператор принимает значение true и элемент списка удаляется, следующая итерация цикла пропускает оценку следующего элемента в списке, поскольку его значения теперь расположены с разными индексами.

Наиболее интуитивно понятное решение - скопировать список, затем выполнить итерацию по исходному списку и изменить только копию.Вы можете попробовать сделать это следующим образом:

L2 = L1
for (a,b) in L1:
    if a < 0 or b < 0 :
        L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)

Однако выходные данные будут идентичны предыдущим:

'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

Это связано с тем, что когда мы создавали L2, python фактически не создавал новый объект.Вместо этого он просто ссылался L2 на тот же объект, что и L1.Мы можем проверить это с помощью 'is', которое отличается от простого "equals" (==).

>>> L2=L1
>>> L1 is L2
True

Мы можем сделать настоящую копию, используя copy.copy().Тогда все работает так, как ожидалось:

import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
    if a < 0 or b < 0 :
        L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

Наконец, есть одно более чистое решение, чем создание совершенно новой копии L1.Функция reversed():

L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
    if a < 0 or b < 0 :
        L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

К сожалению, я не могу адекватно описать, как работает reversed().Он возвращает объект 'listreverseiterator', когда ему передается список.Для практических целей вы можете думать об этом как о создании обратной копии своего аргумента.Это решение, которое я рекомендую.

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

inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]    
for idx, i in enumerate(inlist):
    do some stuff with i['field1']
    if somecondition:
        xlist.append(idx)
for i in reversed(xlist): del inlist[i]

enumerate дает вам доступ к элементу и индексу сразу. полностью изменено так, что индексы, которые вы собираетесь удалить позже, не изменятся для вас.

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

Подробнее смотрите здесь

Вы можете попробовать выполнить цикл в обратном порядке, поэтому для some_list вы сделаете что-то вроде:

list_len = len(some_list)
for i in range(list_len):
    reverse_i = list_len - 1 - i
    cur = some_list[reverse_i]

    # some logic with cur element

    if some_condition:
        some_list.pop(reverse_i)

Таким образом, индекс выравнивается и не страдает от обновлений списка (независимо от того, используете ли вы элемент cur или нет).

Одно из возможных решений, полезное, если вы хотите не только удалить некоторые вещи, но и сделать что-то со всеми элементами в одном цикле:

alist = ['good', 'bad', 'good', 'bad', 'good']
i = 0
for x in alist[:]:
    if x == 'bad':
        alist.pop(i)
        i -= 1
    # do something cool with x or just print x
    print(x)
    i += 1

В большинстве ответов вы хотите, чтобы вы создали копию списка. У меня был случай использования, когда список был довольно длинным (110 тыс. Элементов), и было разумнее продолжать сокращать список.

Прежде всего вам нужно заменить цикл foreach на цикл while ,

i = 0
while i < len(somelist):
    if determine(somelist[i]):
         del somelist[i]
    else:
        i += 1

Значение i не изменяется в блоке if, поскольку вы хотите получить значение нового элемента из того же индекса после удаления старого элемента.

Мне нужно было сделать что-то подобное, и в моем случае проблема заключалась в памяти - мне нужно было объединить несколько объектов dataset в списке, после выполнения некоторых действий с ними, в качестве нового объекта, и мне нужно было избавиться от каждой записи, которую я объединял, чтобы избежать дублирования их всех и увеличения объема памяти.В моем случае наличие объектов в словаре вместо списка работало нормально:

```

k = range(5)
v = ['a','b','c','d','e']
d = {key:val for key,val in zip(k, v)}

print d
for i in range(5):
    print d[i]
    d.pop(i)
print d

```

TLDR:

Я написал библиотеку, которая позволяет вам это делать:

from fluidIter import FluidIterable
fSomeList = FluidIterable(someList)  
for tup in fSomeList:
    if determine(tup):
        # remove 'tup' without "breaking" the iteration
        fSomeList.remove(tup)
        # tup has also been removed from 'someList'
        # as well as 'fSomeList'

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

Должен работать со всеми изменяемыми последовательностями, а не только со списками.


Полный ответ:

Редактировать:Последний пример кода в этом ответе дает пример использования для почему иногда вам может потребоваться изменить список на месте, а не использовать понимание списка.Первая часть ответов служит руководством по как массив может быть изменен на месте.

Решение вытекает из это ответ (на связанный с этим вопрос) от senderle.Это объясняет, как обновляется индекс массива при выполнении итерации по списку, который был изменен.Приведенное ниже решение предназначено для корректного отслеживания индекса массива, даже если список изменен.

Скачать fluidIter.py От здесь https://github.com/alanbacon/FluidIterator, это всего лишь один файл, поэтому нет необходимости устанавливать git.Установщика нет, поэтому вам нужно будет самостоятельно убедиться, что файл находится по пути python.Код был написан для python 3 и не тестировался на python 2.

from fluidIter import FluidIterable
l = [0,1,2,3,4,5,6,7,8]  
fluidL = FluidIterable(l)                       
for i in fluidL:
    print('initial state of list on this iteration: ' + str(fluidL)) 
    print('current iteration value: ' + str(i))
    print('popped value: ' + str(fluidL.pop(2)))
    print(' ')

print('Final List Value: ' + str(l))

Это приведет к следующему результату:

initial state of list on this iteration: [0, 1, 2, 3, 4, 5, 6, 7, 8]
current iteration value: 0
popped value: 2

initial state of list on this iteration: [0, 1, 3, 4, 5, 6, 7, 8]
current iteration value: 1
popped value: 3

initial state of list on this iteration: [0, 1, 4, 5, 6, 7, 8]
current iteration value: 4
popped value: 4

initial state of list on this iteration: [0, 1, 5, 6, 7, 8]
current iteration value: 5
popped value: 5

initial state of list on this iteration: [0, 1, 6, 7, 8]
current iteration value: 6
popped value: 6

initial state of list on this iteration: [0, 1, 7, 8]
current iteration value: 7
popped value: 7

initial state of list on this iteration: [0, 1, 8]
current iteration value: 8
popped value: 8

Final List Value: [0, 1]

Выше мы использовали pop метод для объекта fluid list.Также реализованы другие распространенные итеративные методы, такие как del fluidL[i], .remove, .insert, .append, .extend.Список также может быть изменен с помощью фрагментов (sort и reverse методы не реализованы).

Единственное условие заключается в том, что вы должны изменять список только на месте, если в какой-либо момент fluidL или l если бы код был переназначен другому объекту списка, он бы не работал.Оригинал fluidL object по-прежнему будет использоваться циклом for, но выйдет за рамки наших возможностей для изменения.

т. е.

fluidL[2] = 'a'   # is OK
fluidL = [0, 1, 'a', 3, 4, 5, 6, 7, 8]  # is not OK

Если мы хотим получить доступ к текущему значению индекса списка, мы не можем использовать enumerate, так как это подсчитывает только то, сколько раз выполнялся цикл for.Вместо этого мы будем использовать объект iterator напрямую.

fluidArr = FluidIterable([0,1,2,3])
# get iterator first so can query the current index
fluidArrIter = fluidArr.__iter__()
for i, v in enumerate(fluidArrIter):
    print('enum: ', i)
    print('current val: ', v)
    print('current ind: ', fluidArrIter.currentIndex)
    print(fluidArr)
    fluidArr.insert(0,'a')
    print(' ')

print('Final List Value: ' + str(fluidArr))

Это приведет к следующему результату:

enum:  0
current val:  0
current ind:  0
[0, 1, 2, 3]

enum:  1
current val:  1
current ind:  2
['a', 0, 1, 2, 3]

enum:  2
current val:  2
current ind:  4
['a', 'a', 0, 1, 2, 3]

enum:  3
current val:  3
current ind:  6
['a', 'a', 'a', 0, 1, 2, 3]

Final List Value: ['a', 'a', 'a', 'a', 0, 1, 2, 3]

Тот Самый FluidIterable класс просто предоставляет оболочку для исходного объекта list.К исходному объекту можно получить доступ как к свойству объекта fluid следующим образом:

originalList = fluidArr.fixedIterable

Дополнительные примеры / тесты можно найти в if __name__ is "__main__": раздел в нижней части fluidIter.py.На них стоит обратить внимание, потому что они объясняют, что происходит в различных ситуациях.Такие , как:Замена больших разделов списка с помощью среза.Или использовать (и изменять) одну и ту же итерацию во вложенных циклах for.

Как я уже говорил для начала,:это сложное решение, которое ухудшит читабельность вашего кода и затруднит его отладку.Поэтому другие решения, такие как понимание списка, упомянутые в книге Дэвида Разника ответ это должно быть рассмотрено в первую очередь.Тем не менее, я находил случаи, когда этот класс был мне полезен, и его было проще использовать, чем отслеживать индексы элементов, которые нуждаются в удалении.


Редактировать:Как упоминалось в комментариях, этот ответ на самом деле не представляет проблемы, для которой этот подход обеспечивает решение.Я попытаюсь рассмотреть этот вопрос здесь:

Понимание списков предоставляет способ создания нового списка, но эти подходы, как правило, рассматривают каждый элемент изолированно, а не текущее состояние списка в целом.

т. е.

newList = [i for i in oldList if testFunc(i)]

Но что, если в результате testFunc зависит от элементов, которые были добавлены в newList уже?Или элементы, все еще находящиеся в oldList что может быть добавлено следующим?Возможно, все еще существует способ использовать понимание списка, но он начнет терять свою элегантность, и мне кажется, что проще изменить список на месте.

Приведенный ниже код является одним из примеров алгоритма, который страдает от вышеуказанной проблемы.Алгоритм сократит список таким образом, чтобы ни один элемент не был кратен любому другому элементу.

randInts = [70, 20, 61, 80, 54, 18, 7, 18, 55, 9]
fRandInts = FluidIterable(randInts)
fRandIntsIter = fRandInts.__iter__()
# for each value in the list (outer loop)
# test against every other value in the list (inner loop)
for i in fRandIntsIter:
    print(' ')
    print('outer val: ', i)
    innerIntsIter = fRandInts.__iter__()
    for j in innerIntsIter:
        innerIndex = innerIntsIter.currentIndex
        # skip the element that the outloop is currently on
        # because we don't want to test a value against itself
        if not innerIndex == fRandIntsIter.currentIndex:
            # if the test element, j, is a multiple 
            # of the reference element, i, then remove 'j'
            if j%i == 0:
                print('remove val: ', j)
                # remove element in place, without breaking the
                # iteration of either loop
                del fRandInts[innerIndex]
            # end if multiple, then remove
        # end if not the same value as outer loop
    # end inner loop
# end outerloop

print('')
print('final list: ', randInts)

Выходные данные и окончательный сокращенный список приведены ниже

outer val:  70

outer val:  20
remove val:  80

outer val:  61

outer val:  54

outer val:  18
remove val:  54
remove val:  18

outer val:  7
remove val:  70

outer val:  55

outer val:  9
remove val:  18

final list:  [20, 61, 7, 55, 9]

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

Однако существует один случай, когда безопасно удалить элементы из последовательности, которую вы повторяете: если вы удаляете только один элемент во время итерации. Это можно сделать с помощью return или break . Например:

for i, item in enumerate(lst):
    if item % 4 == 0:
        foo(item)
        del lst[i]
        break

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

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

  

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

Есть пример (получите шансы в кортеже):

list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))  
# result: [1, 5, 9, 15]

Внимание. Вы также не можете обрабатывать итераторы. Итераторы иногда лучше последовательностей.

Я могу придумать три подхода для решения вашей проблемы. В качестве примера я создам случайный список кортежей somelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)] . Условие, которое я выбираю, это сумма элементов кортежа = 15 . В окончательном списке у нас будут только те кортежи, чья сумма не равна 15.

То, что я выбрал, является случайно выбранным примером. Не стесняйтесь изменять список кортежей и условия , которые я выбрал.

Метод 1. > Используйте предложенный вами фреймворк (где код заполняется внутри цикла for). Я использую небольшой код с del , чтобы удалить кортеж, который удовлетворяет указанному условию. Однако этот метод пропускает кортеж (который удовлетворяет указанному условию), если два последовательно размещенных кортежа удовлетворяют данному условию.

for tup in somelist:
    if ( sum(tup)==15 ): 
        del somelist[somelist.index(tup)]

print somelist
>>> [(1, 2, 3), (3, 6, 6), (7, 8, 9), (10, 11, 12)]

Метод 2. > Создайте новый список, который содержит элементы (кортежи), для которых данное условие не выполняется (это то же самое, что удаление элементов списка, где выполняется данное условие) , Ниже приведен код для этого:

newlist1 = [somelist[tup] for tup in range(len(somelist)) if(sum(somelist[tup])!=15)]

print newlist1
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

Метод 3. > Найдите индексы, в которых выполняется данное условие, а затем используйте элементы удаления (кортежи), соответствующие этим индексам. Ниже приведен код для этого.

indices = [i for i in range(len(somelist)) if(sum(somelist[i])==15)]
newlist2 = [tup for j, tup in enumerate(somelist) if j not in indices]

print newlist2
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

Метод 1 и метод 2 быстрее, чем метод 3 . Метод2 и метод3 более эффективны, чем метод1. Я предпочитаю method2 . Для вышеупомянутого примера время (метод1): время (метод2): время (метод3) = 1: 1: 1,7

Цикл

for будет проходить через индекс.

считай, у тебя есть список,

[5, 7, 13, 29, 65, 91]

вы используете переменную списка с именем lis . и вы используете то же самое, чтобы удалить ..

ваша переменная

lis = [5, 7, 13, 29, 35, 65, 91]
       0  1   2   3   4   5   6

во время 5-й итерации,

ваш номер 35 не был простым, поэтому вы удалили его из списка.

lis.remove(y)

, а затем следующее значение (65) переходите к предыдущему индексу.

lis = [5, 7, 13, 29, 65, 91]
       0  1   2   3   4   5

Итак, указатель 4-й итерации выполнен, перемещен на 5-й ...

именно поэтому ваш цикл не покрывает 65 с тех пор, как он переместился в предыдущий индекс.

поэтому вам не следует ссылаться на список в другой переменной, которая по-прежнему ссылается на оригинал, а не на копию.

ite = lis #dont do it will reference instead copy

поэтому скопируйте список, используя list [::]

теперь тебе это дадут,

[5, 7, 13, 29]

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

так что вместо этого вы можете попробовать понимание.

, который поддерживает все повторяющиеся элементы типа like, list, tuple, dict, string и т. д.

Для всего, что может быть действительно большим, я использую следующее.

import numpy as np

orig_list = np.array([1, 2, 3, 4, 5, 100, 8, 13])

remove_me = [100, 1]

cleaned = np.delete(orig_list, remove_me)
print(cleaned)

Это должно быть значительно быстрее, чем все остальное.

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

Вот пример, где предварительное копирование списка некорректно, обратная итерация невозможна, и понимание списка также не вариант.

""" Sieve of Eratosthenes """

def generate_primes(n):
    """ Generates all primes less than n. """
    primes = list(range(2,n))
    idx = 0
    while idx < len(primes):
        p = primes[idx]
        for multiple in range(p+p, n, p):
            try:
                primes.remove(multiple)
            except ValueError:
                pass #EAFP
        idx += 1
        yield p

Если вы позже будете использовать новый список, вы можете просто установить для элемента значение None, а затем оценить его в последующем цикле, например, так

for i in li:
    i = None

for elem in li:
    if elem is None:
        continue

Таким образом, вам не нужно копировать список, и это легче понять.

опишите список чисел, и вы хотите удалить все не делимые на 3,

list_number =[i for i in range(100)]

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

new_list =[i for i in list_number if i%3!=0]

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

new_list = list(filter(lambda x:x%3!=0, list_number))

без использования места в памяти для нового списка и изменения существующего списка

for index, value in enumerate(list_number):
    if list_number[index]%3==0:
        list_number.remove(value)

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

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

Как отметил Дэвид, я рекомендую использовать списки, чтобы сохранить элементы, которые вы не хотите удалять.

somelist = [x for x in somelist if not determine(x)]
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top