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


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

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.


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]

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

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.


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]

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


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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.