Rimuovi più elementi da un elenco Python in una sola istruzione


107

In Python, so come rimuovere elementi da un elenco.

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

Questo codice sopra rimuove i valori 5 e "item" da item_list. Ma quando ci sono molte cose da rimuovere, devo scrivere molte righe

item_list.remove("something_to_remove")

Se conosco l'indice di ciò che sto rimuovendo, utilizzo:

del item_list[x]

dove x è l'indice dell'elemento che voglio rimuovere.

Se conosco l'indice di tutti i numeri che voglio rimuovere, userò una sorta di ciclo per delgli elementi negli indici.

Ma cosa succede se non conosco gli indici degli elementi che voglio rimuovere?

Ho provato item_list.remove('item', 'foo'), ma ho ricevuto un errore che diceva che removerichiede solo un argomento.

C'è un modo per rimuovere più elementi da un elenco in una singola istruzione?

PS ho usato dele remove. Qualcuno può spiegare la differenza tra questi due, o sono la stessa cosa?

Grazie


1
Per rispondere alla tua seconda domanda: delelimina un elemento dal suo indice. La removefunzione di un elenco trova l'indice di un elemento e quindi chiama delquell'indice.
Aaron Christiansen

Risposte:


161

In Python, creare un nuovo oggetto è spesso meglio che modificarne uno esistente:

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

Che è equivalente a:

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

In caso di un grande elenco di valori filtrati (qui, ('item', 5)è un piccolo insieme di elementi), utilizzare a setpuò portare a un miglioramento delle prestazioni, poiché l' inoperazione è in O (1):

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

Si noti che, come spiegato nei commenti e suggerito qui , quanto segue potrebbe far risparmiare ancora più tempo, evitando che il set venga costruito ad ogni loop:

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

Un filtro bloom è anche una buona soluzione se la memoria non è economica.


Mi piace la prima risposta. Non sto creando un nuovo elenco, il che è positivo. Grazie!
RandomCoder

Il set è ottimizzato in python 2 o è ottimizzato solo in python 3? Con questo intendo che il set viene creato solo una volta quando viene generato il bytecode?
Har

Set è, per definizione , ottimizzato per il infunzionamento. Vedi questo benchmark per confronti delle quattro strutture di dati primitive. Sì, il set viene costruito ad ogni loop, come suggerito qui , di conseguenza salvare il set in una variabile dedicata da utilizzare nell'espressione del generatore può far risparmiare tempo.
aluriak

@RandomCoder In realtà crei un nuovo elenco, riutilizzi solo un nome. Puoi verificarlo confrontando id(item_list)prima e dopo item_list = [e for e in item_list if e not in ('item', 5)]. Controlla la mia risposta per vedere come modificare un elenco in atto.
Darkonaut

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

l'elenco finale dopo la rimozione dovrebbe essere il seguente

final_list=[3.14, True]

Codice a riga singola

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

l'output sarebbe il seguente

final_list=[3.14, True]

13
no, mescola gli elementi nell'elenco. Utilizzare solo se l'ordine degli articoli non è importante, cosa che accade abbastanza spesso.
spazzino

5
Ciò rimuoverà anche i duplicati dall'elenco. Non che sia importante per l'elenco degli esempi, però
tschale

1
Questa risposta non è corretta e può causare seri bug, perché è stata votata?
omerfarukdogan

1
Rimuoverà i duplicati! La risposta può causare gravi bug, per favore non usare.
user2698178

2

Non so perché tutti si siano dimenticati di menzionare la straordinaria capacità di sets in Python. Puoi semplicemente inserire la tua lista in un set e quindi rimuovere tutto ciò che desideri rimuovere in una semplice espressione come questa:

>>> 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
Tuttavia, non mantiene l'ordine degli oggetti.
Astrid

E rimuoverà anche i duplicati che potenzialmente esistono nell'elenco.
kyriakosSt

1

Sto ripubblicando la mia risposta da qui perché ho visto che si adatta anche qui. Consente di rimuovere più valori o rimuovere solo i duplicati di questi valori e restituisce un nuovo elenco o modifica l'elenco fornito in posizione.


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

Estensione docstring:

"""
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']
"""

Dovresti essere chiaro su ciò che vuoi veramente fare, modificare un elenco esistente o creare un nuovo elenco con gli elementi specifici mancanti. È importante fare questa distinzione nel caso in cui si dispone di un secondo riferimento che punta all'elenco esistente. Se hai, ad esempio ...

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]

Questo può essere o meno il comportamento che desideri.


1

È possibile utilizzare filterfalse funzione dal itertools modulo

Esempio

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=}")

Produzione:

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

Ma cosa succede se non conosco gli indici degli elementi che voglio rimuovere?

Non capisco esattamente perché non ti piace .remove ma per ottenere il primo indice corrispondente a un valore usa .index (value):

ind=item_list.index('item')

quindi rimuovere il valore corrispondente:

del item_list.pop[ind]

.index (valore) ottiene la prima occorrenza di valore e .remove (valore) rimuove la prima occorrenza di valore


Prendi in considerazione l'utilizzo al del item_list[ind]posto di popse non hai bisogno del valore del risultato.
kojiro

-1

Puoi usarlo -

Supponiamo di avere una lista, l = [1,2,3,4,5]

Vogliamo eliminare gli ultimi due elementi in una singola istruzione

del l[3:]

Abbiamo output:

l = [1,2,3]

Mantienilo semplice

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.