← Все вопросы

Как удалить элемент из списка в Python?

Задан 16 дней назад488 просмотров3 ответа
4

Запутался во всех способах удаления из списка. Есть remove(), pop(), ещё del видел. Чем они отличаются и когда что брать? И самое странное: пробую удалять чётные числа прямо в цикле for, а часть элементов остаётся в списке:

nums = [1, 2, 2, 3, 4, 4]
for n in nums:
    if n % 2 == 0:
        nums.remove(n)
print(nums)  # ожидал [1, 3], получил что-то другое

Почему так и как сделать правильно?

3 ответа

5
✓ Принятый ответ — помог автору

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

Три способа удалить:

lst = ['a', 'b', 'c', 'b']

lst.remove('b')   # удаляет ПЕРВОЕ вхождение значения 'b'
print(lst)        # ['a', 'c', 'b']

x = lst.pop(0)    # удаляет по ИНДЕКСУ и ВОЗВРАЩАЕТ элемент
print(x, lst)     # a ['c', 'b']

del lst[0]        # удаляет по индексу, ничего не возвращает
print(lst)        # ['b']

Коротко: remove — когда знаешь значение, pop — когда знаешь индекс и хочешь забрать элемент (например, как стек: stack.pop() без аргумента снимает последний), del — когда индекс знаешь, а возвращать не нужно (ещё умеет срезы: del lst[1:3]).

Теперь твой баг. Ты меняешь список, по которому прямо сейчас идёшь. Под капотом for хранит внутренний индекс. Удалил элемент — всё, что правее, сдвинулось влево на одну позицию, а индекс шагнул вперёд. В итоге один элемент перепрыгивается. Поэтому второй 2 и второй 4 остаются.

Правильно — не мутировать при итерации, а собрать новый список:

nums = [1, 2, 2, 3, 4, 4]
nums = [n for n in nums if n % 2 != 0]
print(nums)  # [1, 3]

Если нужно изменить именно тот же объект (на него ссылаются в других местах), используй срез-присваивание — оно перезаписывает содержимое:

nums[:] = [n for n in nums if n % 2 != 0]

List comprehension тут и быстрее, и читается лучше любого цикла с remove.

4

Добавлю частую боль: remove и pop кидают исключения, к этому надо быть готовым.

lst = [1, 2, 3]
lst.remove(99)   # ValueError: list.remove(x): x not in list
lst.pop(10)      # IndexError: pop index out of range

Поэтому если не уверен, что значение есть:

if 99 in lst:
    lst.remove(99)

И ещё: remove убирает только ПЕРВОЕ вхождение. Чтобы выкинуть все одинаковые — фильтрация через comprehension, как выше, а не цикл с remove в надежде, что он повторится.

1

Если очень хочется удалять в цикле «на месте» — итерируйся по копии, а меняй оригинал:

nums = [1, 2, 2, 3, 4, 4]
for n in nums[:]:        # nums[:] — копия
    if n % 2 == 0:
        nums.remove(n)
print(nums)  # [1, 3]

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

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект