Entfernen Sie ein Element aus einem Wörterbuch, wenn sein Schlüssel unbekannt ist

112

Was ist der beste Weg, um ein Element nach Wert aus einem Wörterbuch zu entfernen, dh wenn der Schlüssel des Elements unbekannt ist? Hier ist ein einfacher Ansatz:

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

Gibt es bessere Möglichkeiten? Ist etwas falsch daran, Elemente während der Iteration aus dem Wörterbuch zu mutieren (zu löschen)?

Buttons840
quelle
1
Der unterstreichende Grund für das Verbot der Mutation von Diktaten während der Iteration ist, dass intern eine Reihenfolge für die Iteration besteht. Wenn Sie die Schlüssel mutieren, wird die Reihenfolge untergraben, was zu unbekanntem Verhalten führt.
Spectral

Antworten:

92

Beachten Sie, dass Sie derzeit auf Objektidentität testen (wird isnur zurückgegeben, Truewenn beide Operanden durch dasselbe Objekt im Speicher dargestellt werden - dies ist nicht immer bei zwei Objekten der Fall, die mit gleich verglichen werden ==). Wenn Sie dies absichtlich tun, können Sie Ihren Code als neu schreiben

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

Dies kann jedoch nicht das tun, was Sie wollen:

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

Also willst du wahrscheinlich !=statt is not.

Tim Pietzcker
quelle
2
Ist das eine Wörterbuchkomprimierung? Wann wurden sie hinzugefügt?
Buttons840
4
Sie nutzen könnten some_dict.iteritems()hier und stellen forund ifAussagen über separate Leitungen zur besseren Lesbarkeit
JFS
3
Ich glaube, dass in Python 2.7 Wörterbuchverständnisse hinzugefügt wurden.
Mithrandi
2
@JF Sebastian: Ich bin auf Python 3 und bin iteritemses jetzt items. In Python 2.7 iteritems()ist das in der Tat besser.
Tim Pietzcker
1
@ Buttons840 werden in PEP 274 oder Wörterbuchanzeigen als Diktatverständnis bezeichnet . Wie der PEP sagt, wurden sie in 2.7 als Backported 3.x-Heldentaten hinzugefügt . Alternativ können Sie dict()mit einem geeigneten Generatorausdruck füttern , nämlich 2.4. meta: kann hier die peps durchsuchen, um etwas herauszufinden.
n611x007
120

Mit dieser dict.pop(key[, default])Methode können Sie Elemente entfernen, wenn Sie den Schlüssel kennen. Es gibt den Wert am Schlüssel zurück, wenn es das Element entfernt, andernfalls gibt es zurück, als was übergeben wird default. Siehe die Dokumente . '

Beispiel:

>>> 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
quelle
4
OP fragte, wann Schlüssel unbekannt ist
nmz787
52
a = {'name': 'your_name','class': 4}
if 'name' in a: del a['name']
Kracekumar
quelle
OP fragte, wann der Schlüssel unbekannt ist. Diese Antwort setzt voraus, dass der Schlüssel bekannt ist.
Jean-François Corbett
42

Ein einfacher Vergleich zwischen del und 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)

Ergebnis:

0.0329667857143
0.0451040902256

So del ist schneller als pop () .

Luu Tuan Anh
quelle
6
Der Leistungsunterschied ist jedoch nicht groß. Wenn Sie vermeiden möchten, eine Ausnahme auszulösen, können Sie ein zweites Argument angeben pop()(wie oben bei @ n-1-1) - was für den delOperator keine Option ist .
Alex Dupuy
1
Neben der Frage, aber ich hatte auch Schwierigkeiten zu verstehen timeit. Vielen Dank für dieses klare Beispiel.
Adam_G
OP fragte, wann der Schlüssel unbekannt ist. Diese Antwort setzt voraus, dass der Schlüssel bekannt ist.
Jean-François Corbett
7

items()Gibt eine Liste zurück, und es ist diese Liste, die Sie iterieren. Daher spielt es hier keine Rolle, das Diktat in der Schleife zu mutieren. Wenn Sie iteritems()stattdessen verwenden, wäre das Mutieren des Diktats in der Schleife problematisch , und ebenso fürviewitems() Python 2.7.

Ich kann mir keinen besseren Weg vorstellen, um Elemente nach Wert aus einem Diktat zu entfernen.

Mithrandi
quelle
7

Ich würde eine Liste von Schlüsseln erstellen, die entfernt werden müssen, und sie dann entfernen. Es ist einfach, effizient und vermeidet jedes Problem, das Diktat gleichzeitig zu wiederholen und zu mutieren.

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]

quelle
OP fragte, wann der Schlüssel unbekannt ist. Diese Antwort setzt voraus, dass der Schlüssel bekannt ist.
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
quelle
0

Es ist nichts Falsches daran, Elemente aus dem Wörterbuch zu löschen, während Sie iterieren, wie Sie vorgeschlagen haben. Seien Sie vorsichtig, wenn mehrere Threads gleichzeitig dasselbe Wörterbuch verwenden, was zu einem KeyError oder anderen Problemen führen kann.

Weitere Informationen finden Sie in den Dokumenten unter http://docs.python.org/library/stdtypes.html#typesmapping

Thane Hymne
quelle
for k,v in d.iteritems(): del d[k]würde geben RuntimeError: dictionary changed size during iteration. Siehe Mithrandis Erklärung.
Buttons840
1
Natürlich ist d.iteritems () nicht die Iteration des Originalplakats und nicht das, worauf ich mich in meiner Antwort bezog.
Thane Anthem
0

So würde ich es machen.

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