Видаліть декілька елементів зі списку Python лише одним оператором


107

У python я знаю, як видалити елементи зі списку.

item_list = ['item', 5, 'foo', 3.14, True]
item_list.remove('item')
item_list.remove(5)

Цей наведений вище код видаляє значення 5 і 'item' зі списку item_list. Але коли є багато речей для видалення, я повинен написати багато рядків

item_list.remove("something_to_remove")

Якщо я знаю індекс того, що видаляю, використовую:

del item_list[x]

де х - індекс елемента, який я хочу видалити.

Якщо я знаю індекс усіх чисел, які я хочу видалити, я буду використовувати певний цикл для delелементів в індексах.

Але що, якщо я не знаю індексів елементів, які хочу видалити?

Я спробував item_list.remove('item', 'foo'), але я отримав помилку, сказавши, що removeбере лише один аргумент.

Чи є спосіб вилучити кілька елементів зі списку в одній заяві?

PS Я використовував delі remove. Хтось може пояснити різницю між цими двома, або вони однакові?

Дякую


1
Щоб відповісти на ваше друге запитання: delвидаляє елемент за його індексом. removeФункція списку знаходить індексувати елемент , а потім викликає delза цим показником.
Аарон Крістіансен

Відповіді:


159

У Python створити новий об'єкт часто краще, ніж змінити існуючий:

item_list = ['item', 5, 'foo', 3.14, True]
item_list = [e for e in item_list if e not in ('item', 5)]

Що еквівалентно:

item_list = ['item', 5, 'foo', 3.14, True]
new_list = []
for e in item_list:
    if e not in ('item', 5):
        new_list.append(e)
item_list = new_list

У випадку великого списку відфільтрованих значень (тут ('item', 5)малий набір елементів), використання a setможе призвести до підвищення продуктивності, оскільки inоперація знаходиться в O (1):

item_list = [e for e in item_list if e not in {'item', 5}]

Зверніть увагу, що, як пояснюється в коментарях та пропонується тут , наступне може заощадити ще більше часу, уникаючи набору, який будується для кожного циклу:

unwanted = {'item', 5}
item_list = [e for e in item_list if e not in unwanted]

Цвітіння фільтр також є хорошим рішенням , якщо пам'ять не дешево.


Мені подобається перша відповідь. Я не складаю нового списку, що добре. Дякую!
RandomCoder

Чи набір оптимізовано в python 2 чи він оптимізований лише в python 3? Під цим я маю на увазі, чи набір створюється лише один раз, коли генерується байт-код?
Хар

Набір, за визначенням , оптимізований для inроботи. Див. Цей орієнтир для порівняння чотирьох примітивних структур даних. Так, набір будується в кожному циклі, як пропонується тут , отже, збереження набору у виділеній змінній для використання у виразі генератора може заощадити час.
aluriak

@RandomCoder Ви насправді складаєте новий список, ви просто використовуєте ім'я повторно. Ви можете перевірити це, порівнявши id(item_list)до і після item_list = [e for e in item_list if e not in ('item', 5)]. Перевірте мою відповідь, щоб побачити, як змінити список на місці.
Дарконаут

20
item_list = ['item', 5, 'foo', 3.14, True]
list_to_remove=['item', 5, 'foo']

Остаточний список після вилучення повинен бути таким, як показано нижче

final_list=[3.14, True]

Однорядковий код

final_list= list(set(item_list).difference(set(list_to_remove)))

результат буде таким

final_list=[3.14, True]

13
ні, він перемішує елементи у списку. Використовуйте, лише якщо порядок предметів не має значення, що робиться досить часто.
смітник

5
Це також призведе до видалення дублікатів зі списку. Не те, що це має значення для списку прикладів
tschale

1
Ця відповідь неправильна і може спричинити серйозні помилки. Чому вона голосує за?
omerfarukdogan

1
Це видалить дублікати! Відповідь може спричинити серйозні помилки, будь ласка, не використовуйте.
user2698178

2

Не знаю, чому всі забули згадати дивовижні можливості sets в python. Ви можете просто передати свій список у набір, а потім видалити все, що ви хочете видалити, у простому виразі, наприклад:

>>> item_list = ['item', 5, 'foo', 3.14, True]
>>> item_list = set(item_list) - {'item', 5}
>>> item_list
{True, 3.14, 'foo'}
>>> # you can cast it again in a list-from like so
>>> item_list = list(item_list)
>>> item_list
[True, 3.14, 'foo']

6
Однак не підтримує порядок об'єктів.
Астрід

А також видалить дублікати, які потенційно існують у списку.
kyriakosSt

1

Я розміщую свою відповідь звідси, тому що я бачив, що вона також вписується сюди. Це дозволяє видалити кілька значень або видалити лише дублікати цих значень і повертає або новий список, або змінює даний список на місці.


def removed(items, original_list, only_duplicates=False, inplace=False):
    """By default removes given items from original_list and returns
    a new list. Optionally only removes duplicates of `items` or modifies
    given list in place.
    """
    if not hasattr(items, '__iter__') or isinstance(items, str):
        items = [items]

    if only_duplicates:
        result = []
        for item in original_list:
            if item not in items or item not in result:
                result.append(item)
    else:
        result = [item for item in original_list if item not in items]

    if inplace:
        original_list[:] = result
    else:
        return result

Розширення документації:

"""
Examples:
---------

    >>>li1 = [1, 2, 3, 4, 4, 5, 5]
    >>>removed(4, li1)
       [1, 2, 3, 5, 5]
    >>>removed((4,5), li1)
       [1, 2, 3]
    >>>removed((4,5), li1, only_duplicates=True)
       [1, 2, 3, 4, 5]

    # remove all duplicates by passing original_list also to `items`.:
    >>>removed(li1, li1, only_duplicates=True)
      [1, 2, 3, 4, 5]

    # inplace:
    >>>removed((4,5), li1, only_duplicates=True, inplace=True)
    >>>li1
        [1, 2, 3, 4, 5]

    >>>li2 =['abc', 'def', 'def', 'ghi', 'ghi']
    >>>removed(('def', 'ghi'), li2, only_duplicates=True, inplace=True)
    >>>li2
        ['abc', 'def', 'ghi']
"""

Ви повинні чітко розуміти, що ви насправді хочете зробити, змінити існуючий список або скласти новий список із відсутніми конкретними елементами. Важливо провести таку різницю, якщо у вас є друга посилання, що вказує на існуючий список. Якщо у вас є, наприклад ...

li1 = [1, 2, 3, 4, 4, 5, 5]
li2 = li1
# then rebind li1 to the new list without the value 4
li1 = removed(4, li1)
# you end up with two separate lists where li2 is still pointing to the 
# original
li2
# [1, 2, 3, 4, 4, 5, 5]
li1
# [1, 2, 3, 5, 5]

Це може бути, а може і не така поведінка, яку ви бажаєте.


1

Ви можете використовувати filterfalse функцію від itertools модуля

Приклад

import random
from itertools import filterfalse

random.seed(42)

data = [random.randrange(5) for _ in range(10)]
clean = [*filterfalse(lambda i: i == 0, data)]
print(f"Remove 0s\n{data=}\n{clean=}\n")


clean = [*filterfalse(lambda i: i in (0, 1), data)]
print(f"Remove 0s and 1s\n{data=}\n{clean=}")

Вихід:

Remove 0s
data=[0, 0, 2, 1, 1, 1, 0, 4, 0, 4]
clean=[2, 1, 1, 1, 4, 4]

Remove 0s and 1s
data=[0, 0, 2, 1, 1, 1, 0, 4, 0, 4]
clean=[2, 4, 4]

0

Але що, якщо я не знаю індексів елементів, які хочу видалити?

Я точно не розумію, чому вам не подобається .remove, але щоб отримати перший індекс, що відповідає значенню, використовуйте .index (value):

ind=item_list.index('item')

потім видаліть відповідне значення:

del item_list.pop[ind]

.index (value) отримує перше входження значення, а .remove (value) видаляє перше входження значення


Подумайте про використання del item_list[ind]замість того, popякщо вам не потрібно значення результату.
Коджіро

-1

Ви можете використовувати це -

Припустимо, у нас є список, l = [1,2,3,4,5]

Ми хочемо видалити два останні елементи в одному твердженні

del l[3:]

У нас є вихід:

l = [1,2,3]

Не ускладнювати

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.