Elimina un elemento da un dizionario


1396

C'è un modo per eliminare un elemento da un dizionario in Python?

Inoltre, come posso eliminare un elemento da un dizionario per restituire una copia (ovvero, non modificare l'originale)?


13
Perché hai bisogno di una funzione che restituisce un dizionario, quando puoi semplicemente modificare direttamente il dizionario?
amillerrhodes,

5
Il dizionario popmetodo cambia il dizionario sul posto . Pertanto modifica il riferimento al dizionario passato dal chiamante alla "funzione di supporto". Pertanto, la "funzione di supporto" non deve restituire nulla, poiché il riferimento originale al dizionario nel chiamante verrà già modificato. Non assegnare il dict.pop()reso a nulla se non ne hai bisogno. EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Utilizzare deepcopy(my_dict)se necessario.
Mark Mikofski,

1
Poiché il titolo originale non era d'accordo con i dettagli ed escludeva specificamente la soluzione ovvia d.pop(), ho corretto il titolo per porre la domanda specificata nei dettagli.
smci

1
Dovremmo aggiungere un avvertimento chiedendoti se vuoi davvero farlo, come se lo facessi N volte su un dizionario con elementi E perderai (/ userai) memoria O (N * E) con tutte le copie profonde. Se vuoi semplicemente una sola lettura (copia superficiale), fallo d.pop(key). Ma se qualcosa modifica la copia superficiale, hai un problema ben noto con l'aliasing . Aiuta se ci dici il contesto più ampio. (Qualcos'altro sta mai modificando i valori di dict? Stai cercando di scorrere distruttivamente su un elenco? In caso contrario, cosa?)
smci

5
"Perché hai bisogno di una funzione che ritorni un dizionario, quando puoi semplicemente modificare direttamente il dizionario?" Forse perché vuoi scrivere funzioni pure che non modificano i loro parametri?
Gene Callahan,

Risposte:


1729

L' delistruzione rimuove un elemento:

del d[key]

Tuttavia, ciò muta il dizionario esistente, quindi i contenuti del dizionario cambiano per chiunque abbia un riferimento alla stessa istanza. Per restituire un nuovo dizionario, creane una copia:

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

Il dict()costruttore fa una copia superficiale . Per fare una copia profonda, vedere il copymodulo .


Nota che fare una copia per ogni dict del/ incarico / ecc. significa che stai passando da un tempo costante a un tempo lineare e che usi anche lo spazio lineare. Per i piccoli dadi, questo non è un problema. Ma se stai pianificando di fare molte copie di dadi grandi, probabilmente vorrai una struttura dati diversa, come un HAMT (come descritto in questa risposta ).


15
questo è un grande punto sulla mutabilità dei dizionari +1 - anche se non riesco a pensare a un momento in cui volevo copie del dizionario, ho sempre fatto affidamento sul fatto che la copia di "tutti" fosse la stessa. ottimo punto.
tMC

30
@tMC Se modifichi il dictciclo mentre lo attraversi, ti verrà generato un errore:RuntimeError: dictionary changed size during iteration
VertigoRay

15
Che dire del popmetodo che in effetti fa lo stesso? Non è più pitonico? (essendo il metodo di dict, non una parola riservata speciale)?
Serge,

21
Questa risposta ha un punto debole, potrebbe essere fuorviante. I lettori possono fraintendere che dict (d) può dare loro una copia con 'd'. Ma è una copia incompleta. Quando si eseguono solo operazioni con i tasti, va bene. Ma quando vuoi fare qualcos'altro su un dict nidificato, la modifica di 'r' usando quel metodo di copia può causare il cambiamento alla 'd' originale. Per ottenere una copia autentica, devi prima "importare copia", quindi "r = copy.deepcopy (d)".
Zen,

10
@Zen: Abbastanza giusto, ho aggiunto una nota sulla copia superficiale vs. profonda.
Greg Hewgill,

264

pop muta il dizionario.

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

Se vuoi conservare l'originale, puoi semplicemente copiarlo.


29
"del" è ok, ma "pop" sembra più "Pythonic", secondo me.
Ivanleoncz,

1
@ivanleoncz perché?
Kevin

3
poprestituisce il valore "pop-up", che consente di utilizzare questo valore per qualsiasi motivo ulteriore. Se non è più "Pythonic", direi che sembra meglio, sicuramente :). Non è un dict, ma funziona allo stesso modo per entrambi: github.com/ivanlmj/python-prototypes/blob/master/3.4/…
ivanleoncz

2
@ivanleoncz È anche meglio per un motivo in più, poppuò essere fornito con un valore predefinito che verrà restituito quando manca una chiave da dict. È utile quando è necessario rimuovere alcune chiavi, ma alcune potrebbero non essere disponibili; delgetterebbe KeyErrorin tal caso.
Itachi l'

80

Penso che la tua soluzione sia il modo migliore per farlo. Ma se vuoi un'altra soluzione, puoi creare un nuovo dizionario usando le chiavi del vecchio dizionario senza includere la chiave specificata, in questo modo:

>>> 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
Veramente cool. Mi piace il metodo rapido per filtrare un dizionario senza definire una nuova funzione.
Joe J,

6
Per coloro che non hanno familiarità con le comprensioni, puoi anche fare qualcosa del genere: {i:a[i] for i in a if i not in [0, 1, 2]}se vuoi rimuovere diversi elementi.
kmatheny,

9
Meglio sarebbe {k:v for k,v in a.items() if k != 0}penso.
sabato

1
La migliore soluzione per la rimozione di un articolo per chiave e la restituzione del risultato del nuovo dict nella stessa riga. Ad esempio, se è necessario utilizzare un dict già costruito senza un singolo elemento come **kwargs,some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
Cole

La migliore soluzione qui. Un liner e non muta il dizionario originale.
Andrew Winterbotham,

55

La dichiarazione del è quello che stai cercando. Se hai un dizionario chiamato foo con un tasto chiamato 'bar', puoi eliminare 'bar' da foo in questo modo:

del foo['bar']

Si noti che ciò modifica permanentemente il dizionario su cui si opera. Se desideri conservare il dizionario originale, dovrai crearne una copia in anticipo:

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

La dictchiamata fa una copia superficiale. Se si desidera una copia profonda, utilizzare copy.deepcopy.

Ecco un metodo che puoi copiare e incollare, per tua comodità:

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

1
@ pythonian29033, in realtà no . La risposta accettata funziona come previsto: restituisce il dict senza una chiave. L'approccio da questa risposta muta il dict originale;) C'è una differenza significativa
maxkoryukov,

1
@ arussell84, perché >>>viene spesso usato negli esempi python? Sì, python-doc contiene molte cose del genere. Ma tale codice non è conveniente per copypaste . Sono confuso ...
maxkoryukov,

@maxkoryukov yup lo fa! ma quella funzione e questa risposta sono esattamente le stesse, con l'eccezione che quella risposta è all'interno di una funzione. e non devi aver codificato in Python per un po ', >>>imita la notazione di ascolto da Python in modalità cli
pythonian29033

2
@ pythonian29033 about >>>. Sì, è in stile REPL, ma parliamo francamente: l'unico uomo aveva scritto questo esempio e 1000 l'hanno letto. Penso che sarebbe bello scrivere esempi nel modo che consenta una facile copia ed esecuzione. Non mi piace rimuovere manualmente queste parentesi angolari. O copia riga per riga .. Quindi non capisco: perché questi angoli sono ancora lì))) Forse non so qualcosa?
maxkoryukov,

3
Ho aggiunto una funzione che può essere copiata / incollata, per tua comodità.
arussell84,

48

Ci sono molte belle risposte, ma voglio sottolineare una cosa.

È possibile utilizzare sia il dict.pop()metodo sia delun'istruzione più generica per rimuovere elementi da un dizionario. Entrambi mutano il dizionario originale, quindi è necessario crearne una copia (vedere i dettagli di seguito).

Ed entrambi genereranno un KeyErrorse la chiave che stai fornendo loro non è presente nel dizionario:

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

Devi occuparti di questo:

catturando l'eccezione:

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)

eseguendo un controllo:

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)

ma con pop()c'è anche un modo molto più conciso: fornisci il valore di ritorno predefinito:

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

A meno che non si utilizzi pop()per ottenere il valore di una chiave rimossa, è possibile fornire qualsiasi cosa, non necessaria None. Anche se potrebbe essere che l'utilizzo delcon incheck sia leggermente più veloce a causa della pop()sua funzione con le sue complicazioni che causano spese generali. Di solito non è così, quindi pop()con il valore predefinito è abbastanza buono.


Per quanto riguarda la domanda principale, dovrai fare una copia del tuo dizionario, per salvare il dizionario originale e averne uno nuovo senza che la chiave venga rimossa.

Alcune altre persone qui suggeriscono di fare una copia completa (in profondità) copy.deepcopy(), che potrebbe essere eccessiva, una copia "normale" (superficiale), usando copy.copy()o dict.copy(), potrebbe essere sufficiente. Il dizionario mantiene un riferimento all'oggetto come valore per una chiave. Pertanto, quando si rimuove una chiave da un dizionario, questo riferimento viene rimosso, non l'oggetto a cui si fa riferimento. L'oggetto stesso può essere rimosso in seguito automaticamente dal Garbage Collector, se non ci sono altri riferimenti nella memoria. Realizzare una copia profonda richiede più calcoli rispetto alla copia superficiale, quindi diminuisce le prestazioni del codice effettuando la copia, sprecando memoria e fornendo più lavoro al GC, a volte è sufficiente una copia superficiale.

Tuttavia, se si hanno oggetti mutabili come valori di dizionario e si prevede di modificarli in un secondo momento nel dizionario restituito senza la chiave, è necessario effettuare una copia approfondita.

Con copia superficiale:

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}

Con copia profonda:

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

... come posso eliminare un elemento da un dizionario per restituire una copia (ovvero, non modificare l'originale)?

A dictè la struttura di dati errata da utilizzare per questo.

Certo, copiare il dict e saltar fuori dalla copia funziona, così come la costruzione di un nuovo dict con una comprensione, ma tutto ciò che la copia richiede tempo - hai sostituito un'operazione a tempo costante con una lineare. E tutte quelle copie vive allo stesso tempo occupano spazio, spazio lineare per copia.

Altre strutture di dati, come i tentativi mappati dell'array di hash , sono progettate esattamente per questo tipo di caso d'uso: l'aggiunta o la rimozione di un elemento restituisce una copia in tempo logaritmico, condividendo la maggior parte della sua memoria con l'originale . 1

Naturalmente ci sono alcuni aspetti negativi. Le prestazioni sono logaritmiche piuttosto che costanti (sebbene con una base ampia, di solito 32-128). E, sebbene sia possibile rendere identica l'API non mutante, l'API dict"mutante" è ovviamente diversa. E, soprattutto, non ci sono batterie HAMT incluse con Python. 2

La pyrsistentlibreria è un'implementazione piuttosto solida delle sostituzioni di dict (e vari altri tipi) basate su HAMT per Python. Ha persino un'API evolutiva elegante per il porting del codice mutante esistente sul codice persistente nel modo più fluido possibile. Ma se vuoi essere esplicito sulla restituzione delle copie piuttosto che sulla mutazione, lo usi semplicemente in questo modo:

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

Questo d3 = d1.remove('a')è esattamente ciò che la domanda sta ponendo.

Se hai strutture di dati mutabili come dicte listincorporate in pmap, avrai comunque problemi di aliasing: puoi risolverlo solo andando immutabile fino in fondo, incorporando pmaps e pvectors.


1. Gli HAMT sono anche diventati popolari in linguaggi come Scala, Clojure, Haskell perché giocano molto bene con la programmazione senza blocco e la memoria transazionale del software, ma nessuno di questi è molto rilevante in Python.

2. Infatti, v'è un HAMT nel stdlib, impiegati per l'attuazione di contextvars. Il PEP ritirato in precedenza spiega perché. Ma questo è un dettaglio di implementazione nascosto della libreria, non un tipo di raccolta pubblica.


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

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


14

Chiama semplicemente del d ['chiave'].

Tuttavia, in produzione, è sempre buona norma verificare se 'chiave' esiste in d.

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

7
Hmm, no, in produzione è meglio seguire l' ideologia EAFP . Basta rimuovere la chiave nel try-exceptblocco. Almeno, questa sarà un'operazione atomica;)
maxkoryukov,

1
E se vuoi essere conciso - usa d.pop('key', None), è oneliner. Ma la vera domanda era di ottenere il dizionario senza una chiave e non di modificare il dict. Quindi comprensioni - è una buona scelta qui;)
maxkoryukov,

7

No, non esiste altro

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

Tuttavia, spesso la creazione di copie di dizionari solo leggermente modificati non è probabilmente una buona idea perché comporterà richieste di memoria relativamente grandi. Di solito è meglio registrare il vecchio dizionario (se necessario) e quindi modificarlo.


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}
>>>

questo non fa alcun errore nella gestione, presuppone che la chiave sia nel dict, si consiglia di verificarlo prima e raisese non lo è


10
In che modo il tuo metodo è diverso dal semplice del test_dict[key]?
Fisico pazzo,

5

Ecco un approccio progettuale di alto livello:

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

Sto passando il dizionario e la chiave che desidero nella mia funzione, convalida se si tratta di un dizionario e se la chiave è a posto, e se esistono entrambe, rimuove il valore dal dizionario e stampa i resti.

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

Spero che aiuti!


3

Di seguito lo snippet di codice ti aiuterà sicuramente, ho aggiunto commenti in ogni riga che ti aiuteranno a capire il codice.

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

oppure puoi anche usare dict.pop ()

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

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

o l'approccio migliore è

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

Ecco un'altra variante usando la comprensione dell'elenco:

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

L'approccio si basa su una risposta da questo post: modo efficiente per rimuovere le chiavi con stringhe vuote da un dict


1
Se hai intenzione di rispondere a una domanda vecchia di anni che ha già una risposta semplice, appropriata e accettata, almeno assicurati che la tua risposta sia giusta. Ciò non fa ciò che l'OP ha richiesto.
user2357112 supporta Monica il

In genere non controllo le date sulle domande che ritengo possano essere aggiunte informazioni preziose. Inoltre, per uno dei commenti sulla domanda che ho collegato: "Di solito questo è esattamente ciò che qualcuno vuole ed è probabilmente ciò di cui l'OP ha bisogno, ma non è ciò che l'OP ha richiesto" stackoverflow.com/questions/12118695/… I sapeva che non era una risposta diretta alla domanda; piuttosto un'espansione alle opzioni.
BigBlueHat,

3
Questa risposta, sebbene non completa, ci consente di apprendere che possiamo rimuovere anche gli elementi con condizioni if. sto solo cambiando if vper if k is not 'a'rispondere all'op. Ma non penso che sia un modo efficiente, questo rimuove l'elemento in O (n) piuttosto che O (log n) come pop o 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)}}

Il codice seguente crea una copia di dict speciesed elimina gli elementi che non sono presentitrans_HI

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