Remova um item de um dicionário quando sua chave for desconhecida

112

Qual é a melhor maneira de remover um item de um dicionário por valor, ou seja, quando a chave do item é desconhecida? Esta é uma abordagem simples:

for key, item in some_dict.items():
    if item is item_to_remove:
        del some_dict[key]

Existem maneiras melhores? Há algo de errado em alterar (excluir itens) do dicionário durante a iteração?

Buttons840
fonte
1
O motivo sublinhado para proibir a mutação de dict durante a iteração é porque internamente há uma ordem para a iteração. Se você alterar as chaves, a ordem será prejudicada, o que resulta em um comportamento desconhecido.
Espectral de
Possível duplicata de Como remover uma chave de um dicionário Python?
tripleee

Respostas:

92

Esteja ciente de que você está testando atualmente a identidade do objeto ( isretorna apenas Truese os dois operandos forem representados pelo mesmo objeto na memória - nem sempre é o caso com dois objetos que são comparados iguais a ==). Se você estiver fazendo isso de propósito, poderá reescrever seu código como

some_dict = {key: value for key, value in some_dict.items() 
             if value is not value_to_remove}

Mas isso pode não fazer o que você deseja:

>>> some_dict = {1: "Hello", 2: "Goodbye", 3: "You say yes", 4: "I say no"}
>>> value_to_remove = "You say yes"
>>> some_dict = {key: value for key, value in some_dict.items() if value is not value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 3: 'You say yes', 4: 'I say no'}
>>> some_dict = {key: value for key, value in some_dict.items() if value != value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 4: 'I say no'}

Então você provavelmente quer em !=vez de is not.

Tim Pietzcker
fonte
2
Isso é uma compressão de dicionário? Quando eles foram adicionados?
Buttons840
4
você poderia usar some_dict.iteritems()aqui e colocar as instruções fore ifem linhas separadas para
facilitar a
3
Acredito que as compreensões de dicionário foram adicionadas no Python 2.7.
mithrandi
2
@JF Sebastian: Estou no Python 3 e iteritemsagora estou items. No Python 2.7, iteritems()é realmente melhor.
Tim Pietzcker
1
@ Buttons840 são chamados de compreensões de dicionário no PEP 274 ou telas de dicionário . como o PEP diz, eles foram adicionados em 2.7 como talentos backported 3.x. como alternativa, você pode alimentar dict()com uma expressão geradora apropriada, que é 2.4. meta: pode navegar pelos peps aqui para descobrir coisas.
n611x007
120

O dict.pop(key[, default])método permite remover itens quando você conhece a chave. Ele retorna o valor na chave se remover o item, caso contrário, retorna o que foi passado como default. Veja a documentação . '

Exemplo:

>>> dic = {'a':1, 'b':2}
>>> dic
{'a': 1, 'b': 2}
>>> dic.pop('c', 0)
0
>>> dic.pop('a', 0)
1
>>> dic
{'b': 2}
N 1.1
fonte
4
OP questionado sobre quando a chave é desconhecida
nmz787
52
a = {'name': 'your_name','class': 4}
if 'name' in a: del a['name']
Kracekumar
fonte
OP perguntou quando a chave é desconhecida. Esta resposta assume que a chave é conhecida.
Jean-François Corbett
42

Uma comparação simples entre del e pop () :

import timeit
code = """
results = {'A': 1, 'B': 2, 'C': 3}
del results['A']
del results['B']
"""
print timeit.timeit(code, number=100000)
code = """
results = {'A': 1, 'B': 2, 'C': 3}
results.pop('A')
results.pop('B')
"""
print timeit.timeit(code, number=100000)

resultado:

0.0329667857143
0.0451040902256

Portanto, del é mais rápido que pop () .

Luu Tuan Anh
fonte
6
No entanto, a diferença de desempenho não é grande e, se quiser evitar o lançamento de uma exceção, você pode fornecer um segundo argumento para pop()(como @ n-1-1 acima) - que não é uma opção para o deloperador.
Alex Dupuy
1
Auxiliar à pergunta, mas também tenho lutado para entender timeit. Obrigado por este exemplo claro.
Adam_G
OP perguntou quando a chave é desconhecida. Esta resposta assume que a chave é conhecida.
Jean-François Corbett
7

items()retorna uma lista, e é essa lista que você está iterando, portanto, alterar o dict no loop não importa aqui. Se você estivesse usando em iteritems()vez disso, alterar o dict no loop seria problemático , e da mesma forma para viewitems()no Python 2.7.

Não consigo pensar em uma maneira melhor de remover itens de um dicionário por valor.

mithrandi
fonte
7

Eu construiria uma lista de chaves que precisam ser removidas e, em seguida, removeria. É simples, eficiente e evita qualquer problema de iteração simultânea e mutação do dict.

keys_to_remove = [key for key, value in some_dict.iteritems()
                  if value == value_to_remove]
for key in keys_to_remove:
    del some_dict[key]

fonte
OP perguntou quando a chave é desconhecida. Esta resposta assume que a chave é conhecida.
Jean-François Corbett
1
y={'username':'admin','machine':['a','b','c']}
if 'c' in y['machine'] : del y['machine'][y['machine'].index('c')]
user3559640
fonte
0

Não há nada de errado em excluir itens do dicionário durante a iteração, como você propôs. Tenha cuidado com vários threads usando o mesmo dicionário ao mesmo tempo, o que pode resultar em um KeyError ou outros problemas.

Claro, consulte os documentos em http://docs.python.org/library/stdtypes.html#typesmapping

Thane Anthem
fonte
for k,v in d.iteritems(): del d[k]daria RuntimeError: dictionary changed size during iteration. Veja a explicação de mithrandi.
Buttons840
1
Obviamente, d.iteritems () não é como o autor da postagem original está iterando e não é o que eu estava me referindo em minha resposta.
Thane Anthem
0

É assim que eu faria.

for key in some_dict.keys():
    if some_dict[key] == item_to_remove:
        some_dict.pop(key)
        break
Nathan
fonte