Удалить все вхождения значения из списка?


378

В Python remove()удалит первое вхождение значения в списке.

Как удалить все вхождения значения из списка?

Вот что я имею в виду:

>>> remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
[1, 3, 4, 3]

Ответы:


507

Функциональный подход:

Python 3.x

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter((2).__ne__, x))
[1, 3, 3, 4]

или

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter(lambda a: a != 2, x))
[1, 3, 3, 4]

Python 2.x

>>> x = [1,2,3,2,2,2,3,4]
>>> filter(lambda a: a != 2, x)
[1, 3, 3, 4]

120
Используйте понимание списка через фильтр + лямбда; первый более читабелен в дополнение к более эффективному.
habnabit

17
с / , как правило / вообще быть /
habnabit

99
Код для предложения habnabit выглядит следующим образом:[y for y in x if y != 2]
coredumperror

8
Я бы не назвал это решение лучшим. Постижения списков быстрее и легче понять, просматривая код. Это скорее будет скорее Perl, чем Python.
Питер Нимрут

3
-1 для прямого вызова __ne__. Сравнение двух значений - гораздо более сложный процесс, чем просто вызов __eq__или __ne__одно из них. Здесь это может работать правильно, потому что вы сравниваете только цифры, но в общем случае это неверно и является ошибкой.
Аран-Фей

212

Вы можете использовать понимание списка:

def remove_values_from_list(the_list, val):
   return [value for value in the_list if value != val]

x = [1, 2, 3, 4, 2, 2, 3]
x = remove_values_from_list(x, 2)
print x
# [1, 3, 4, 3]

7
Как бы вы удалили элементы, не проверяя их?
Александр Юнгберг

18
Это не изменяет исходный список, но возвращает новый список.
Джон Y

6
@Selinap: Нет, это оптимально, так как сканирует список только один раз. В вашем исходном коде inоператор и removeметод сканируют весь список (до тех пор, пока они не найдут совпадение), так что в итоге вы сканируете список несколько раз таким образом.
Джон Кугельман

4
@mhawke, @John Y: просто используйте x [:] = ... вместо x =, и это будет «на месте», а не просто повторное связывание имени «x» (скорость, по сути, та же, и НАМНОГО быстрее, чем x .может быть !!!).
Алекс Мартелли

10
Я голосую за это, потому что после 6 лет Python я все еще не понимаю Lambdas :)
Бенджамин

107

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

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> x[:] = (value for value in x if value != 2)
>>> x
[1, 3, 4, 3]

1
@Selinap: фильтр не изменяет список, он возвращает новый список.
EM

Фильтр и списки не изменяют список. назначение среза делает. и оригинальный пример делает.
А.Коди

7
Мне это нравится, потому что он изменяет список, на который ссылается x. Если есть какие-либо другие ссылки на этот список, они также будут затронуты. Это в отличие от x = [ v for v in x if x != 2 ]предложений, которые создают новый список и изменяют x, чтобы ссылаться на него, оставляя исходный список нетронутым.
Ханнес

40

Повторим решение первого поста более абстрактно:

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> while 2 in x: x.remove(2)
>>> x
[1, 3, 4, 3]

19
Это O (n * n), хотя.
Ханнес

@ Ханнес не будет ли это O (n), поскольку он проходит цикл только один раз и одновременно удаляет элемент?
пент

1
Посмотрим x = [1] * 10000 + [2] * 1000. Тело цикла выполняется 1000 раз, и .remove () должен пропускать 10000 элементов при каждом вызове. Это пахнет O (n * n) для меня, но это не доказательство. Я думаю, что доказательством было бы предположить, что число 2 в списке пропорционально его длине. Этот коэффициент пропорциональности затем исчезает в обозначении big-O. В лучшем случае, если в списке только постоянное число 2, это не O (n ^ 2), а O (2n), то есть O (n).
Ханнес

23

Смотрите простое решение

>>> [i for i in x if i != 2]

Это вернет список, содержащий все элементы xбез2


11

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

>>> import random, timeit
>>> a = list(range(5)) * 1000
>>> random.shuffle(a)

>>> b = a
>>> print(b is a)
True

>>> b = [x for x in b if x != 0]
>>> print(b is a)
False
>>> b.count(0)
0
>>> a.count(0)
1000

>>> b = a
>>> b = filter(lambda a: a != 2, x)
>>> print(b is a)
False

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

Чтобы изменить список на месте, используйте метод, подобный этому

>>> def removeall_inplace(x, l):
...     for _ in xrange(l.count(x)):
...         l.remove(x)
...
>>> removeall_inplace(0, b)
>>> b is a
True
>>> a.count(0)
0

Что касается скорости, результаты на моем ноутбуке таковы (все в списке из 5000 записей с удалением 1000 записей)

  • Понимание списка - ~ 400us
  • Фильтр - ~ 900 мкс
  • .remove () loop - 50мс

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

>>> def removeall_replace(x, l):
....    t = [y for y in l if y != x]
....    del l[:]
....    l.extend(t)
  • removeall_replace () - 450us

Почему бы тогда просто не переназначить новый список по старому адресу? def remove_all(x, l): return [y for y in l if y != x]затемl = remove_all(3,l)
Даннид

@Dannid Это второй метод в первом окне кода. Он создает новый список, а вы не изменяете старый список. Любые другие ссылки на список останутся без фильтра.
Пол С

Ах, верно. Я так увлекся определением метода, что упустил простое назначение, которое вы уже сделали.
Даннид

7

ты можешь сделать это

while 2 in x:   
    x.remove(2)

3
Это плохое решение, так как список должен быть пройден 2 * n раз для n случаев из 2.
cxxl

Не рекомендуется добавлять или удалять из списка, который вы просматриваете. Плохая практика ИМХО.
Аман Матур

5

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

x = [1, 2, 3, 4, 2, 2, 3]
def remove_values_from_list(the_list, val):
    for i in range(the_list.count(val)):
        the_list.remove(val)

remove_values_from_list(x, 2)

print(x)

Для списка, который вы показываете в своем коде, этот подход примерно на 36% медленнее, чем метод понимания списка (который возвращает копию), согласно моим измерениям.
диджеит

Хорошо, что вы это заметили. Тем не менее, поскольку я думаю, что это могло ускользнуть от вашего суждения, я сравнивал свою версию с самым первым предложением автора вопроса.
Мартин Андерссон

4

Подход и временная привязка к списку / массиву с 1.000.000 элементов:

Тайминги:

In [10]: a.shape
Out[10]: (1000000,)

In [13]: len(lst)
Out[13]: 1000000

In [18]: %timeit a[a != 2]
100 loops, best of 3: 2.94 ms per loop

In [19]: %timeit [x for x in lst if x != 2]
10 loops, best of 3: 79.7 ms per loop

Вывод: NumPy в 27 раз быстрее (на моем ноутбуке) по сравнению с подходом к списку

PS, если вы хотите преобразовать свой обычный список Python lstв массив numpy:

arr = np.array(lst)

Настроить:

import numpy as np
a = np.random.randint(0, 1000, 10**6)

In [10]: a.shape
Out[10]: (1000000,)

In [12]: lst = a.tolist()

In [13]: len(lst)
Out[13]: 1000000

Проверьте:

In [14]: a[a != 2].shape
Out[14]: (998949,)

In [15]: len([x for x in lst if x != 2])
Out[15]: 998949

4
a = [1, 2, 2, 3, 1]
to_remove = 1
a = [i for i in a if i != to_remove]
print(a)

Возможно, не самый питонический, но все же самый легкий для меня, ха-ха


3

Чтобы удалить все повторяющиеся вхождения и оставить их в списке:

test = [1, 1, 2, 3]

newlist = list(set(test))

print newlist

[1, 2, 3]

Вот функция, которую я использовал для Project Euler:

def removeOccurrences(e):
  return list(set(e))

2
Мне нужно было сделать это для вектора со значениями 250 КБ, и это работает как шарм.
rschwieb

1
Ответ: да! И я полностью понимаю, если иметь вектор, который долго звучит совершенно безумно для компетентного программиста. Я рассматриваю проблемы там как математик, не беспокоясь об оптимизации решений, и это может привести к решениям дольше, чем номинал. (Хотя у меня нет терпения для решений дольше 5 минут.)
rschwieb

6
Это удалит любой порядок из списка.
asmeurer

4
@JaredBurrows возможно потому, что он не отвечает на вопрос в его нынешнем виде, но это совсем другой вопрос.
drevicko

6
-1, это не ответ на вопрос ОП. Это решение для удаления дубликатов, а это совершенно другой вопрос.
Анойз

2

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

category_ids.sort()
ones_last_index = category_ids.count('1')
del category_ids[0:ones_last_index]

2
Я понимаю, куда вы идете, но этот код не будет работать, так как вам нужен также стартовый индекс, а не просто 0.
Шедокан,

2
for i in range(a.count(' ')):
    a.remove(' ')

Гораздо проще, я верю.


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

2

Позволять

>>> x = [1, 2, 3, 4, 2, 2, 3]

Самое простое и эффективное решение, которое уже было опубликовано ранее:

>>> x[:] = [v for v in x if v != 2]
>>> x
[1, 3, 4, 3]

Другая возможность, которая должна использовать меньше памяти, но медленнее,

>>> for i in range(len(x) - 1, -1, -1):
        if x[i] == 2:
            x.pop(i)  # takes time ~ len(x) - i
>>> x
[1, 3, 4, 3]

Результаты синхронизации для списков длиной 1000 и 100000 с 10% совпадающими записями: 0,16 против 0,25 мс и 23 против 123 мс.

Сроки с длиной 1000

Сроки с длиной 100000


1

Удалить все вхождения значения из списка Python

lists = [6.9,7,8.9,3,5,4.9,1,2.9,7,9,12.9,10.9,11,7]
def remove_values_from_list():
    for list in lists:
      if(list!=7):
         print(list)
remove_values_from_list()

Результат: 6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11

С другой стороны,

lists = [6.9,7,8.9,3,5,4.9,1,2.9,7,9,12.9,10.9,11,7]
def remove_values_from_list(remove):
    for list in lists:
      if(list!=remove):
        print(list)
remove_values_from_list(7)

Результат: 6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11


Msgstr "Python" вложен для каждого цикла if внутри функции, работающей со 100% точностью! "
rafiqul786

Вы не изменяете список, вы просто печатаете элементы. Кроме того, называть список списками непонятно
kon psych

0

Если у вас нет встроенного filterили вы не хотите использовать дополнительное пространство, и вам нужно линейное решение ...

def remove_all(A, v):
    k = 0
    n = len(A)
    for i in range(n):
        if A[i] !=  v:
            A[k] = A[i]
            k += 1

    A = A[:k]

0
hello =  ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
#chech every item for a match
for item in range(len(hello)-1):
     if hello[item] == ' ': 
#if there is a match, rebuild the list with the list before the item + the list after the item
         hello = hello[:item] + hello [item + 1:]
print hello

['Привет мир']


Пожалуйста, попробуйте разработать свой ответ с объяснением.
parlad

0

Я просто сделал это для списка. Я просто новичок. Немного более продвинутый программист наверняка может написать такую ​​функцию.

for i in range(len(spam)):
    spam.remove('cat')
    if 'cat' not in spam:
         print('All instances of ' + 'cat ' + 'have been removed')
         break

0

Мы также можем сделать на месте удалить все , используя либо delили pop:

import random

def remove_values_from_list(lst, target):
    if type(lst) != list:
        return lst

    i = 0
    while i < len(lst):
        if lst[i] == target:
            lst.pop(i)  # length decreased by 1 already
        else:
            i += 1

    return lst

remove_values_from_list(None, 2)
remove_values_from_list([], 2)
remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
lst = remove_values_from_list([random.randrange(0, 10) for x in range(1000000)], 2)
print(len(lst))

Теперь для эффективности:

In [21]: %timeit -n1 -r1 x = random.randrange(0,10)
1 loop, best of 1: 43.5 us per loop

In [22]: %timeit -n1 -r1 lst = [random.randrange(0, 10) for x in range(1000000)]
g1 loop, best of 1: 660 ms per loop

In [23]: %timeit -n1 -r1 lst = remove_values_from_list([random.randrange(0, 10) for x in range(1000000)]
    ...: , random.randrange(0,10))
1 loop, best of 1: 11.5 s per loop

In [27]: %timeit -n1 -r1 x = random.randrange(0,10); lst = [a for a in [random.randrange(0, 10) for x in
    ...:  range(1000000)] if x != a]
1 loop, best of 1: 710 ms per loop

Как мы видим, версия на месте remove_values_from_list()не требует дополнительной памяти, но для ее запуска требуется гораздо больше времени:

  • 11 секунд для удаления значений на месте
  • 710 миллисекунд для списочного понимания, которое выделяет новый список в памяти

0

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

Временная сложность: O (n)
Дополнительная сложность пространства: O (1)

def main():
    test_case([1, 2, 3, 4, 2, 2, 3], 2)     # [1, 3, 3, 4]
    test_case([3, 3, 3], 3)                 # []
    test_case([1, 1, 1], 3)                 # [1, 1, 1]


def test_case(test_val, remove_val):
    remove_element_in_place(test_val, remove_val)
    print(test_val)


def remove_element_in_place(my_list, remove_value):
    length_my_list = len(my_list)
    swap_idx = length_my_list - 1

    for idx in range(length_my_list - 1, -1, -1):
        if my_list[idx] == remove_value:
            my_list[idx], my_list[swap_idx] = my_list[swap_idx], my_list[idx]
            swap_idx -= 1

    for pop_idx in range(length_my_list - swap_idx - 1):
        my_list.pop() # O(1) operation


if __name__ == '__main__':
    main()

-1

О скорости!

import time
s_time = time.time()

print 'start'
a = range(100000000)
del a[:]
print 'finished in %0.2f' % (time.time() - s_time)
# start
# finished in 3.25

s_time = time.time()
print 'start'
a = range(100000000)
a = []
print 'finished in %0.2f' % (time.time() - s_time)
# start
# finished in 2.11

-3
p=[2,3,4,4,4]
p.clear()
print(p)
[]

Только с Python 3


2
Весело, это в рамках поставленного вопроса и является правильным.
Эрих

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

-3

Что случилось с:

Motor=['1','2','2']
For i in Motor:
       If i  != '2':
       Print(i)
Print(motor)

Использование анаконды


2
Пожалуйста, объясните ваши строки кода, чтобы другие пользователи могли понять его функциональность. Спасибо!
Игнасио Ара

Этот код ничего не удалит из списка.
Георгий
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.