Rimuove un elemento da un dizionario quando la sua chiave è sconosciuta


112

Qual è il modo migliore per rimuovere un elemento da un dizionario in base al valore, ad esempio quando la chiave dell'elemento è sconosciuta? Ecco un approccio semplice:

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

Esistono modi migliori? C'è qualcosa di sbagliato nel mutare (eliminare elementi) dal dizionario durante l'iterazione?


1
Il motivo sottolineato per vietare la modifica di dict durante l'iterazione è perché internamente c'è un ordine per l'iterazione, se si modificano le chiavi, l'ordine sarebbe indebolito, il che risulta in un comportamento sconosciuto.
Spectral

Risposte:


92

Tieni presente che stai attualmente testando l'identità dell'oggetto ( isrestituisce solo Truese entrambi gli operandi sono rappresentati dallo stesso oggetto in memoria - questo non è sempre il caso di due oggetti che si confrontano allo stesso modo ==). Se lo fai apposta, potresti riscrivere il codice come

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

Ma questo potrebbe non fare quello che vuoi:

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

Quindi probabilmente vuoi !=invece di is not.


2
È una compressione del dizionario? Quando sono stati aggiunti?
Pulsanti840

4
potresti usare some_dict.iteritems()qui e mettere fore ifdichiarazioni su righe separate per leggibilità
jfs

3
Credo che la comprensione del dizionario sia stata aggiunta in Python 2.7.
mithrandi

2
@ JF Sebastian: sono su Python 3, e iteritemsora lo è items. In Python 2.7, iteritems()è davvero migliore.
Tim Pietzcker

1
@ Buttons840 sono chiamate comprensioni dict in PEP 274 o display del dizionario . come dice il PEP, sono stati aggiunti nella 2.7 come prodezze 3.x con backport. in alternativa puoi alimentare dict()con un'espressione del generatore appropriata, che è 2.4. meta: puoi sfogliare i pep qui per scoprire cose.
n611x007

120

Il dict.pop(key[, default])metodo ti consente di rimuovere gli elementi quando conosci la chiave. Restituisce il valore alla chiave se rimuove l'elemento altrimenti restituisce ciò che è stato passato come default. Vedi i documenti . "

Esempio:

>>> dic = {'a':1, 'b':2}
>>> dic
{'a': 1, 'b': 2}
>>> dic.pop('c', 0)
0
>>> dic.pop('a', 0)
1
>>> dic
{'b': 2}

4
OP ha chiesto in merito a quando la chiave è sconosciuta
nmz787

52
a = {'name': 'your_name','class': 4}
if 'name' in a: del a['name']

OP ha chiesto quando la chiave è sconosciuta. Questa risposta presuppone che la chiave sia nota.
Jean-François Corbett

42

Un semplice confronto tra 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)

risultato:

0.0329667857143
0.0451040902256

Quindi, del è più veloce di pop () .


6
Tuttavia, la differenza di prestazioni non è eccezionale e, se si desidera evitare di sollevare un'eccezione, è possibile fornire un secondo argomento a pop()(come fa @ n-1-1 sopra), che non è un'opzione per l' deloperatore.
Alex Dupuy

1
Accessorio alla domanda, ma stavo anche lottando per capire timeit. Grazie per questo chiaro esempio.
Adam_G

OP ha chiesto quando la chiave è sconosciuta. Questa risposta presuppone che la chiave sia nota.
Jean-François Corbett

7

items()restituisce una lista, ed è quella lista che stai iterando, quindi cambiare il dict nel ciclo non ha importanza qui. Se invece si stesse usando iteritems(), la modifica del dict nel ciclo sarebbe problematico , e lo stesso accade viewitems()in Python 2.7.

Non riesco a pensare a un modo migliore per rimuovere elementi da un dettato in base al valore.


7

Creerei un elenco di chiavi che devono essere rimosse, quindi le rimuoverò. È semplice, efficiente ed evita qualsiasi problema di iterazione e modifica simultanea del 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]

OP ha chiesto quando la chiave è sconosciuta. Questa risposta presuppone che la chiave sia nota.
Jean-François Corbett


1
y={'username':'admin','machine':['a','b','c']}
if 'c' in y['machine'] : del y['machine'][y['machine'].index('c')]

0

Non c'è niente di sbagliato nell'eliminare elementi dal dizionario durante l'iterazione, come hai proposto. Fare attenzione a più thread che utilizzano lo stesso dizionario contemporaneamente, il che potrebbe causare un KeyError o altri problemi.

Ovviamente, vedere i documenti su http://docs.python.org/library/stdtypes.html#typesmapping


for k,v in d.iteritems(): del d[k]darebbe RuntimeError: dictionary changed size during iteration. Vedi la spiegazione di mithrandi.
Pulsanti840

1
Naturalmente, d.iteritems () non è il modo in cui il poster originale sta iterando, e non ciò a cui mi riferivo nella mia risposta.
Thane Anthem

0

Ecco come lo farei.

for key in some_dict.keys():
    if some_dict[key] == item_to_remove:
        some_dict.pop(key)
        break
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.