Remova vários itens de uma lista Python em apenas uma instrução

107

Em python, sei como remover itens de uma lista.

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

Este código acima remove os valores 5 e 'item' de item_list. Mas quando há muitas coisas para remover, tenho que escrever muitas linhas de

item_list.remove("something_to_remove")

Se eu souber o índice do que estou removendo, eu uso:

del item_list[x]

onde x é o índice do item que desejo remover.

Se eu souber o índice de todos os números que desejo remover, usarei algum tipo de loop para delos itens nos índices.

Mas e se eu não souber os índices dos itens que desejo remover?

Eu tentei item_list.remove('item', 'foo'), mas recebi um erro dizendo que removesó leva um argumento.

Existe uma maneira de remover vários itens de uma lista em uma única instrução?

PS Eu usei dele remove. Alguém pode explicar a diferença entre esses dois, ou eles são iguais?

obrigado

RandomCoder
fonte
1
Para responder à sua segunda pergunta: delexclui um item por seu índice. A removefunção de uma lista encontra o índice de um item e, em seguida, chama delesse índice.
Aaron Christiansen
Possível duplicata de Excluir vários elementos de uma lista
IMLD de

Respostas:

159

Em Python, criar um novo objeto geralmente é melhor do que modificar um existente:

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

O que é 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

No caso de uma grande lista de valores filtrados (aqui, ('item', 5)é um pequeno conjunto de elementos), usar um setpode levar a uma melhoria de desempenho, pois a inoperação está em O (1):

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

Observe que, conforme explicado nos comentários e sugerido aqui , o seguinte pode economizar ainda mais tempo, evitando que o conjunto seja construído a cada loop:

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

Um filtro bloom também é uma boa solução se a memória não for barata.

Aluriak
fonte
Eu gosto da primeira resposta. Não estou fazendo uma nova lista, o que é bom. Obrigado!
RandomCoder
O conjunto é otimizado em python 2 ou é otimizado apenas em python 3? Com isso quero dizer que o conjunto é criado apenas uma vez quando o bytecode é gerado?
Har
Set é, por definição , otimizado para inoperação. Veja estes benchmarks para comparações das quatro estruturas de dados primitivas. Sim, o conjunto é construído a cada loop, conforme sugerido aqui , consequentemente salvar o conjunto em uma variável dedicada para usar na expressão geradora pode economizar tempo.
aluriak de
@RandomCoder Você realmente faz uma nova lista, apenas reutiliza um nome. Você pode verificar isso comparando id(item_list)antes e depois item_list = [e for e in item_list if e not in ('item', 5)]. Verifique minha resposta para ver como modificar uma lista no local.
Darkonaut
20
item_list = ['item', 5, 'foo', 3.14, True]
list_to_remove=['item', 5, 'foo']

a lista final após a remoção deve ser a seguinte

final_list=[3.14, True]

Código de Linha Única

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

a saída seria a seguinte

final_list=[3.14, True]
Aakash Goel
fonte
13
não, ele embaralha os itens da lista. Use apenas se a ordem dos itens não for importante, o que acontece com frequência.
catador de
5
Isso também removerá duplicatas da lista. Não que isso importe para a lista de exemplos
tschale de
1
Esta resposta está incorreta e pode causar erros graves, por que foi votada positivamente?
omerfarukdogan de
1
Isso removerá duplicatas! A resposta pode causar bugs graves, por favor, não use.
user2698178
2

Não sei por que todos se esqueceram de mencionar a incrível capacidade do sets em python. Você pode simplesmente lançar sua lista em um conjunto e, em seguida, remover tudo o que deseja remover em uma expressão simples como:

>>> 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']
Anwarvic
fonte
6
Porém, não mantém a ordem dos objetos.
Astrid
E também removerá duplicatas que potencialmente existem na lista.
kyriakosSt
1

Estou postando minha resposta a partir daqui porque vi que também se encaixa aqui. Ele permite remover vários valores ou remover apenas duplicatas desses valores e retorna uma nova lista ou modifica a lista fornecida no local.


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

Extensão 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']
"""

Você deve ser claro sobre o que realmente deseja fazer, modificar uma lista existente ou fazer uma nova lista com os itens específicos ausentes. É importante fazer essa distinção no caso de você ter uma segunda referência apontando para a lista existente. Se você tem, por exemplo ...

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]

Este pode ser ou não o comportamento que você deseja.

Darkonauta
fonte
1

Você pode usar a função filterfalse do módulo itertools

Exemplo

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

Resultado:

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]
Vlad Bezden
fonte
0

Mas e se eu não souber os índices dos itens que desejo remover?

Não entendo exatamente por que você não gosta de .remove, mas para obter o primeiro índice correspondente a um valor, use .index (valor):

ind=item_list.index('item')

em seguida, remova o valor correspondente:

del item_list.pop[ind]

.index (valor) obtém a primeira ocorrência de valor e .remove (valor) remove a primeira ocorrência de valor

aless80
fonte
Considere usar em del item_list[ind]vez de popse não precisar do valor do resultado.
kojiro
-1

Você pode usar isto -

Suponha que temos uma lista, l = [1,2,3,4,5]

Queremos deletar os dois últimos itens em uma única declaração

del l[3:]

Temos saída:

l = [1,2,3]

Mantenha simples

Krishna Singhal
fonte