Excluir um elemento de um dicionário


1396

Existe uma maneira de excluir um item de um dicionário em Python?

Além disso, como posso excluir um item de um dicionário para retornar uma cópia (ou seja, sem modificar o original)?


13
Por que você precisa de uma função que retorne um dicionário, quando você pode apenas modificar o dicionário diretamente?
Amillerrhodes

5
O método de dicionáriopop altera o dicionário no local . Portanto, altera a referência ao dicionário que foi passado do chamador para a "função auxiliar". Portanto, a "função auxiliar" não precisa retornar nada, pois a referência original ao dicionário no chamador já será alterada. Não atribua o retorno de dict.pop()nada se você não precisar. EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Use deepcopy(my_dict)se necessário.
Mark Mikofski

1
Como o título original discordava dos detalhes e excluía especificamente a solução óbvia d.pop(), eu o corrigi para fazer a pergunta especificada nos detalhes.
smci 14/05

1
Devemos adicionar uma advertência perguntando se você realmente deseja fazer isso, como se o fizesse N vezes em um dicionário com elementos E, você vazaria (/ usará) memória O (N * E) com todas as cópias profundas. Se você deseja apenas uma leitura somente (cópia superficial), faça d.pop(key). Mas se alguma coisa modificar a cópia superficial, você terá um problema conhecido com o alias . Ajuda se você nos contar o contexto mais amplo. (É qualquer outra coisa que nunca modificar os valores de dicionários Você está tentando destrutivamente iterar sobre uma lista se não, o quê???)
SMCI

5
"Por que você precisa de uma função que retorne um dicionário, quando você pode apenas modificar o dicionário diretamente?" Talvez porque você queira escrever funções puras que não modifiquem seus parâmetros?
Gene Callahan

Respostas:


1729

A delinstrução remove um elemento:

del d[key]

No entanto, isso modifica o dicionário existente para que o conteúdo do dicionário seja alterado para qualquer pessoa que tenha uma referência à mesma instância. Para retornar um novo dicionário, faça uma cópia do dicionário:

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r

O dict()construtor faz uma cópia superficial . Para fazer uma cópia detalhada, consulte o copymódulo .


Note que fazer uma cópia para cada ditado del/ tarefa / etc. significa que você está indo do tempo constante para o tempo linear e também usando espaço linear. Para ditados pequenos, isso não é um problema. Mas se você planeja fazer muitas cópias de dictos grandes, provavelmente deseja uma estrutura de dados diferente, como um HAMT (conforme descrito nesta resposta ).


15
esse é um ponto importante sobre a mutabilidade dos dicionários +1 - embora eu não consiga pensar em uma época em que desejasse cópias do dicionário, sempre confiei na cópia de 'todos'. ótimo ponto.
TMC

30
@tMC Se você editar o dictque estiver percorrendo, ele RuntimeError: dictionary changed size during iteration
causará

15
E o popmétodo que de fato faz o mesmo? Não é mais pitônico? (sendo o método do ditado, não uma palavra reservada especial)?
Serge

21
Esta resposta tem uma fraqueza, pode ser enganosa. Os leitores podem entender mal que dict (d) pode fornecer uma cópia com 'd'. Mas é uma cópia incompleta. Ao fazer apenas operações com chaves del, tudo bem. Mas quando você deseja fazer outra coisa em um ditado aninhado, modificar 'r' usando esse método de cópia pode causar alterações no 'd' original. Para obter uma cópia autêntica, é necessário primeiro 'importar cópia' e depois 'r = copy.deepcopy (d)'.
Zen

10
@ Zen: Justo, eu adicionei uma nota sobre cópia superficial e profunda.
22814 Greg Hewgill

264

pop muda o dicionário.

 >>> lol = {"hello": "gdbye"}
 >>> lol.pop("hello")
     'gdbye'
 >>> lol
     {}

Se você quiser manter o original, basta copiá-lo.


29
"del" está ok, mas "pop" parece mais "pitonico", na minha opinião.
Ivanleoncz

1
@ivanleoncz por quê?
Kevr

3
popretorna o valor que foi 'popped', o que permite que você use esse valor por qualquer outro motivo. Se não é mais "Pythonic", eu diria que parece melhor, com certeza :). Não é um ditado, mas funciona da mesma maneira para ambos: github.com/ivanlmj/python-prototypes/blob/master/3.4/…
ivanleoncz

2
@ivanleoncz Também é melhor, por mais um motivo, poppode ser fornecido com um valor padrão que será retornado quando uma chave estiver ausente do dict. É bom quando você precisa remover algumas chaves, mas algumas delas podem estar ausentes; deljogaria KeyErrornesse caso.
itachi

80

Eu acho que sua solução é a melhor maneira de fazê-lo. Mas se você quiser outra solução, poderá criar um novo dicionário usando as chaves do dicionário antigo, sem incluir a chave especificada, assim:

>>> a
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> {i:a[i] for i in a if i!=0}
{1: 'one', 2: 'two', 3: 'three'}

2
Realmente legal. Eu gosto do método rápido para filtrar um dicionário sem definir uma nova função.
21138 Joe J

6
Para aqueles que não estão familiarizados com as compreensões, também é possível fazer algo assim: {i:a[i] for i in a if i not in [0, 1, 2]}se você deseja remover vários elementos.
kmatheny

9
Melhor seria {k:v for k,v in a.items() if k != 0}eu acho.
rlbond

1
A melhor solução para remover um item por chave e retornar o resultado do novo ditado na mesma linha. Por exemplo, se você precisa usar um um dict já construídos sem um único item como **kwargs,some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
Cole

Melhor solução aqui. Um forro e não muda o dicionário original.
Andrew Winterbotham

55

A declaração del é o que você está procurando. Se você tem um dicionário chamado foo com uma chave chamada 'bar', é possível excluir 'bar' de foo assim:

del foo['bar']

Observe que isso modifica permanentemente o dicionário que está sendo operado. Se você deseja manter o dicionário original, precisará criar uma cópia com antecedência:

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}

A dictchamada faz uma cópia superficial. Se você quiser uma cópia profunda, use copy.deepcopy.

Aqui está um método que você pode copiar e colar, para sua conveniência:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy

1
@ pythonian29033, na verdade, não . A resposta aceita funciona como esperado - retorna o ditado sem uma chave. A abordagem a partir desta resposta mutates dict original;) Há uma diferença significat
maxkoryukov

1
@ arussell84, por que >>>é frequentemente usado nos exemplos de python? Sim, o python-doc contém muitas dessas coisas. Mas esse código não é conveniente para copypaste . Estou confuso ...
maxkoryukov 28/05

@maxkoryukov yup it does! mas essa função e essa resposta são exatamente as mesmas, com exceção da resposta estar dentro de uma função. e você não deve ter sido codificação em python por um tempo, os >>>imita a notação de escuta de python no modo cli
pythonian29033

2
@ pythonian29033 sobre >>>. Sim, é estilo REPL, mas vamos falar francamente: o único homem que escreveu essa amostra e 1000 leram isso. Eu acho que seria ótimo escrever exemplos da maneira que permite copiar e executar com facilidade. Não gosto de remover esses colchetes à mão. Ou copie linha por linha .. Então, eu não entendo: por que esses ângulos ainda estão lá)))) Talvez eu não saiba de algo?
Maxkoryukov 29/05

3
Eu adicionei uma função que pode ser copiada / colada, para sua conveniência.
Arussell84

48

Há muitas respostas legais, mas quero enfatizar uma coisa.

Você pode usar o dict.pop()método e uma delinstrução mais genérica para remover itens de um dicionário. Ambos mudam o dicionário original, então você precisa fazer uma cópia (veja detalhes abaixo).

E os dois irão gerar um KeyErrorse a chave que você está fornecendo a eles não estiver presente no dicionário:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`

e

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove)  # Raises `KeyError: 'c'`

Você tem que cuidar disso:

capturando a exceção:

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    del d[key_to_remove]
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

e

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    d.pop(key_to_remove)
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

executando uma verificação:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]

e

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)

mas pop()também há uma maneira muito mais concisa - forneça o valor de retorno padrão:

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove, None)  # No `KeyError` here

A menos que você use pop()para obter o valor de uma chave sendo removida, você pode fornecer qualquer coisa, não necessário None. Embora possa ser que o uso delcom incheque seja um pouco mais rápido devido a pop()uma função com suas próprias complicações causando sobrecarga. Normalmente não é o caso, portanto, pop()com o valor padrão é bom o suficiente.


Quanto à pergunta principal, você terá que fazer uma cópia do seu dicionário, salvar o dicionário original e ter um novo sem a chave ser removida.

Algumas outras pessoas aqui sugerem fazer uma cópia completa (profunda) com copy.deepcopy(), que pode ser um exagero, uma cópia "normal" (superficial), usando copy.copy()ou dict.copy(), pode ser suficiente. O dicionário mantém uma referência ao objeto como um valor para uma chave. Portanto, quando você remove uma chave de um dicionário, essa referência é removida, não o objeto que está sendo referenciado. O próprio objeto pode ser removido posteriormente automaticamente pelo coletor de lixo, se não houver outras referências para ele na memória. Fazer uma cópia profunda requer mais cálculos em comparação com a cópia superficial; portanto, diminui o desempenho do código ao fazer a cópia, desperdiçando memória e fornecendo mais trabalho ao GC; às vezes, a cópia superficial é suficiente.

No entanto, se você tiver objetos mutáveis ​​como valores do dicionário e planeja modificá-los posteriormente no dicionário retornado sem a chave, precisará fazer uma cópia profunda.

Com cópia rasa:

def get_dict_wo_key(dictionary, key):
    """Returns a **shallow** copy of the dictionary without a key."""
    _dict = dictionary.copy()
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

Com cópia profunda:

from copy import deepcopy


def get_dict_wo_key(dictionary, key):
    """Returns a **deep** copy of the dictionary without a key."""
    _dict = deepcopy(dictionary)
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

21

… Como posso excluir um item de um dicionário para retornar uma cópia (ou seja, sem modificar o original)?

A dicté a estrutura de dados incorreta a ser usada para isso.

Certamente, copiar o ditado e saltar da cópia funciona, assim como construir um novo ditado com compreensão, mas toda essa cópia leva tempo - você substituiu uma operação de tempo constante por uma linear. E todas essas cópias vivas ao mesmo tempo ocupam espaço - espaço linear por cópia.

Outras estruturas de dados, como tentativas mapeadas de matriz de hash , são projetadas exatamente para esse tipo de caso de uso: adicionar ou remover um elemento retorna uma cópia em tempo logarítmico, compartilhando a maior parte de seu armazenamento com o original . 1

Claro que existem algumas desvantagens. O desempenho é logarítmico e não constante (embora com uma base grande, geralmente de 32 a 128). E, embora você possa tornar a API sem mutação idêntica à dict, a API "mutante" é obviamente diferente. E, acima de tudo, não há baterias HAMT incluídas no Python. 2

A pyrsistentbiblioteca é uma implementação bastante sólida de substituições de dict baseadas em HAMT (e vários outros tipos) para Python. Ele ainda possui uma API do evolver bacana para transportar o código mutante existente para o código persistente da maneira mais suave possível. Mas se você quiser ser explícito sobre o retorno de cópias em vez de fazer a mutação, use-o assim:

>>> from pyrsistent import m
>>> d1 = m(a=1, b=2)
>>> d2 = d1.set('c', 3)
>>> d3 = d1.remove('a')
>>> d1
pmap({'a': 1, 'b': 2})
>>> d2
pmap({'c': 3, 'a': 1, 'b': 2})
>>> d3
pmap({'b': 2})

É d3 = d1.remove('a')exatamente isso que a pergunta está pedindo.

Se você tiver estruturas de dados mutáveis ​​como dicte listincorporadas ao pmap, ainda terá problemas de alias - você só pode consertar isso imutável até o fim, incorporando pmaps e pvectors.


1. Os HAMTs também se tornaram populares em linguagens como Scala, Clojure e Haskell, porque jogam muito bem com programação livre de bloqueios e memória transacional de software, mas nenhum deles é muito relevante no Python.

2. Na verdade, não é uma Hamt no stdlib, utilizados na execução de contextvars. O PEP retirado anteriormente explica o porquê. Mas esse é um detalhe de implementação oculto da biblioteca, não um tipo de coleção pública.


19
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

Resultado: d = {1: 2, '2': 3}


14

Simplesmente chame del d ['chave'].

No entanto, na produção, é sempre uma boa prática verificar se 'chave' existe em d.

if 'key' in d:
    del d['key']

7
Hmm, não, na produção é melhor seguir a ideologia do EAFP . Basta remover a chave no try-exceptbloco. Pelo menos, esta será uma operação atômica;)
maxkoryukov

1
E se você quer ser conciso d.pop('key', None), use , é um delineador. Mas a questão real era sobre obter o dicionário sem uma chave e não modificar o ditado. Então compreensões - é uma boa escolha aqui;)
maxkoryukov

7

Não, não há outra maneira senão

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy

No entanto, criar cópias de dicionários apenas ligeiramente alterados provavelmente não é uma boa ideia, pois resultará em demandas de memória comparativamente grandes. Geralmente é melhor registrar o dicionário antigo (se necessário) e modificá-lo.


7
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)

5
>>> def delete_key(dict, key):
...     del dict[key]
...     return dict
... 
>>> test_dict = {'one': 1, 'two' : 2}
>>> print delete_key(test_dict, 'two')
{'one': 1}
>>>

isso não faz nenhum tratamento de erros, assume que a chave está no ditado, você pode verificar isso primeiro e raisese não estiver


10
Como o seu método é diferente do que apenas del test_dict[key]?
Mad Physicist

5

Aqui está uma abordagem de design de nível superior:

def eraseElement(d,k):
    if isinstance(d, dict):
        if k in d:
            d.pop(k)
            print(d)
        else:
            print("Cannot find matching key")
    else:
        print("Not able to delete")


exp = {'A':34, 'B':55, 'C':87}
eraseElement(exp, 'C')

Estou passando o dicionário e a chave que desejo para minha função, valida se é um dicionário e se a chave está correta e, se ambas existirem, remove o valor do dicionário e imprime as sobras.

Resultado: {'B': 55, 'A': 34}

Espero que ajude!


3

Abaixo o snippet de código o ajudará definitivamente, adicionei comentários em cada linha que ajudarão você a entender o código.

def execute():
   dic = {'a':1,'b':2}
   dic2 = remove_key_from_dict(dic, 'b')  
   print(dict2)           # {'a': 1}
   print(dict)            # {'a':1,'b':2}

def remove_key_from_dict(dictionary_to_use, key_to_delete):
   copy_of_dict = dict(dictionary_to_use)     # creating clone/copy of the dictionary
   if key_to_delete in copy_of_dict :         # checking given key is present in the dictionary
       del copy_of_dict [key_to_delete]       # deleting the key from the dictionary 
   return copy_of_dict                        # returning the final dictionary

ou você também pode usar dict.pop ()

d = {"a": 1, "b": 2}

res = d.pop("c")  # No `KeyError` here
print (res)       # this line will not execute

ou a melhor abordagem é

res = d.pop("c", "key not found")
print (res)   # key not found
print (d)     # {"a": 1, "b": 2}

res = d.pop("b", "key not found")
print (res)   # 2
print (d)     # {"a": 1}

2

Aqui está outra variação usando a compreensão da lista:

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.iteritems() if v)
# result should be {'b': 'Some'}

A abordagem é baseada em uma resposta deste post: Maneira eficiente de remover chaves com strings vazias de um dict


1
Se você deseja responder a uma pergunta de anos que já possui uma resposta simples, apropriada e aceita, pelo menos verifique se a resposta está correta. Isso não faz o que o OP pediu.
User2357112 suporta Monica

Geralmente, não verifico as datas das perguntas que acho que poderiam ter informações valiosas adicionadas a elas. Além disso, por um dos comentários sobre a pergunta a que vinculei: "Normalmente, é exatamente isso que alguém deseja e provavelmente é o que o OP precisa, mas não é o que o OP solicitou" stackoverflow.com/questions/12118695/… I sabia que não era uma resposta direta à pergunta; antes uma expansão para as opções.
precisa saber é o seguinte

3
Essa resposta, embora não esteja completa, permite-nos aprender que também podemos remover itens com condições if. apenas mudando if vpara if k is not 'a'responder ao op. Mas eu não acho que seja uma maneira eficiente, isso remove o elemento em O (n) em vez de O (log n) como pop ou del.
holgac

0
    species = {'HI': {'1': (1215.671, 0.41600000000000004),
  '10': (919.351, 0.0012),
  '1025': (1025.722, 0.0791),
  '11': (918.129, 0.0009199999999999999),
  '12': (917.181, 0.000723),
  '1215': (1215.671, 0.41600000000000004),
  '13': (916.429, 0.0005769999999999999),
  '14': (915.824, 0.000468),
  '15': (915.329, 0.00038500000000000003),
 'CII': {'1036': (1036.3367, 0.11900000000000001), '1334': (1334.532, 0.129)}}

O código a seguir fará uma cópia do dict speciese excluirá os itens que não estão notrans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
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.