Манипуляция списком в Python с pop ()

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

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

Пример моей проблемы:

L = ['a', 'b', 'c', 'd'] for i in range(len(L)): print L if L[i] == 'a' or L[i] == 'c': L.pop(i) 

3 Solutions collect form web for “Манипуляция списком в Python с pop ()”

Являются ли ваши списки большими? Если это так, используйте ifilter из itertools чтобы отфильтровать элементы, которые вам не нужны лениво (без itertools стоимости).

Списки не такие большие? Просто используйте понимание списка:

  newlist = [x for x in oldlist if x not in ['a', 'c'] ] 

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

Как удобная среда синтаксиса удобство и лень (= эффективность для больших списков), вы можете построить генератор, а не список, используя ( ) вместо [ ] :

 interestingelts = (x for x in oldlist if x not in ['a', 'c']) 

После этого вы можете перебирать interestingelts , но вы не можете индексировать его:

  for y in interestingelts: # ok print y print interestingelts[0] # not ok: generator allows sequential access only 

Вы хотите понять список:

 L = [c for c in L if c not in ['a', 'c']] 

Или, если вы действительно не хотите создавать копию, вернитесь назад:

 for i in reversed(range(len(L))): if L[i] in ['a', 'c']: L.pop(i) # del L[i] is more efficient 

Благодаря ncoghlan для reversed() и phooji для del L[i] предложений. (Я решил оставить его как L.pop(i) , так как именно так изначально был сформулирован вопрос).

Кроме того, как справедливо указывает Дж. С. Себастьян, движение назад – это пространство, но время неэффективно; большую часть времени лучше всего понимает или генератор списка ( L = (...) вместо L = [...] ).

Редактировать:

Итак, так как люди, кажется, хотят чего-то менее смехотворно медленного, чем обратный метод выше (я не могу себе представить, почему … 🙂 вот фильтр сохранения на месте, который должен отличаться по скорости от понимания списка только константа. (Это похоже на то, что я сделал бы, если бы захотел отфильтровать строку в c.)

 write_i = 0 for read_i in range(len(L)): L[write_i] = L[read_i] if L[read_i] not in ['a', 'c']: write_i += 1 del L[write_i:] print L # output: ['b', 'd'] 

Резюме

  • использование списка (или genexpr) для удаления нескольких элементов из списка
  • если ваш ввод является большой байтовой строкой, то используйте str.translate() для удаления символов
  • удаление одного элемента во время del L[i] медленнее для больших списков

Если элементы являются байтами, как в вашем примере, вы можете использовать str.translate() :

 def remove_bytes(bytestr, delbytes): """ >>> remove_bytes(b'abcd', b'ac') == b'bd' True """ return bytestr.translate(None, delbytes) 

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

 def remove_inplace_without_order(L, delitems): """Remove all items from `L` that are in `delitems` (not preserving order). >>> L = list(range(4)); remove_inplace_without_order(L, [0,2]); L [3, 1] """ idel = len(L) # items idel.. to be removed for i in reversed(range(len(L))): if L[i] in delitems: idel -= 1 L[i] = L[idel] # save `idel`-th item del L[idel:] # remove items all at once #NOTE: the function returns `None` (it means it modifies `L` inplace) 

Поскольку @phooji и @senderle уже упомянутое понимание списка (или выражение генератора) предпочтительнее в вашем случае:

 def remove_listcomp(L, delitems): return [x for x in L if x not in delitems] 

Вот сравнение производительности для L=list("abcd"*10**5); delitems="ac" L=list("abcd"*10**5); delitems="ac" :

 | function | time, msec | ratio | |------------------------------+------------+--------| | list | 4.42 | 0.9 | | remove_bytes | 4.88 | 1.0 | | remove | 27.3 | 5.6 | | remove_listcomp | 36.8 | 7.5 | | remove_inplace_without_order | 71.2 | 14.6 | | remove_inplace_senderle2 | 83.8 | 17.2 | | remove_inplace_senderle | 15000 | 3073.8 | #+TBLFM: $3=$2/@3$2;%.1f 

где

 try: from itertools import ifilterfalse as filterfalse except ImportError: from itertools import filterfalse # py3k def remove(L, delitems): return filterfalse(delitems.__contains__, L) def remove_inplace_senderle(L, delitems): for i in reversed(range(len(L))): if L[i] in delitems: del L[i] def remove_inplace_senderle2(L, delitems): write_i = 0 for read_i in range(len(L)): L[write_i] = L[read_i] if L[read_i] not in delitems: write_i += 1 del L[write_i:] 

remove_inplace_senderle() медленный из-за использования алгоритма O(N**2) . Каждый del L[i] может привести к тому, что все элементы вправо будут перемещены влево, чтобы закрыть промежуток.

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

Здесь приведены тайминги для одного и того же ввода, но без создания нового списка на каждой итерации:

  | function | time, msec | ratio | |-----------------+------------+-------| | remove_bytes | 0.391 | 1 | | remove | 24.3 | 62 | | remove_listcomp | 33.4 | 85 | #+TBLFM: $3=$2/@2$2;%d 

В таблице показано, что itertools.ifilterfalse() не обеспечивает значительного улучшения по сравнению с listcomp.

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

  • изменяемый тип внутри неизменяемого контейнера
  • Разница между использованием и list () в Python
  • удаление конечных пустых элементов в Python
  • Python Список массивов np для массива
  • python выбирает определенные элементы из списка
  • Фильтрация списков: понимание списка по сравнению с lambda + filter
  • Как добавить список во второй список (списки конкатенаций)
  • Определите, присутствуют ли все элементы в списке и в том же порядке в другом списке
  • Удалите четное / нечетное число из нечетного / четного списка Python.
  • «Python way» для синтаксического анализа и условной замены каждого элемента в 2D-списке
  • Создайте пустой список в python с определенным размером
  • Python - лучший язык программирования в мире.