Frage Wie lösche ich Elemente aus einem Wörterbuch, während ich darüber iteriere?


Ist es legitim, Elemente aus einem Wörterbuch in Python zu löschen, während man darüber iteriert?

Beispielsweise:

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]

Die Idee besteht darin, Elemente, die eine bestimmte Bedingung nicht erfüllen, aus dem Wörterbuch zu entfernen, anstatt ein neues Wörterbuch zu erstellen, das eine Teilmenge desjenigen ist, über das iteriert wird.

Ist das eine gute Lösung? Gibt es elegantere / effizientere Wege?


204
2018-03-21 23:23


Ursprung


Antworten:


Ein einfacher Test in der Konsole zeigt, dass Sie ein Wörterbuch nicht ändern können, während Sie darüber iterieren:

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k, v in mydict.iteritems():
...    if k == 'two':
...        del mydict[k]
...
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Wie in der Antwort von delnan erwähnt, verursacht das Löschen von Einträgen Probleme, wenn der Iterator versucht, auf den nächsten Eintrag zu wechseln. Verwenden Sie stattdessen die keys() Methode, um eine Liste der Schlüssel zu erhalten und damit zu arbeiten:

>>> for k in mydict.keys():
...    if k == 'two':
...        del mydict[k]
...
>>> mydict
{'four': 4, 'three': 3, 'one': 1}

Wenn Sie basierend auf dem Wert der Elemente löschen müssen, verwenden Sie den Befehl items() Methode stattdessen:

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]
...
>>> mydict
{'four': 4, 'one': 1}

BEARBEITEN:

Oben wird nicht funktionieren für Python3 und geben ein RuntimeError.

RuntimeError: Das Wörterbuch hat die Größe während der Iteration geändert.

Dies geschieht, weil mydict.keys() Gibt einen Iterator und keine Liste zurück. Wie in den Kommentaren darauf hingewiesen wird einfach konvertieren mydict.keys() zu einer Liste von list(mydict.keys()) und es sollte funktionieren.


239
2018-03-21 23:47



Sie können es auch in zwei Schritten tun:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

Mein Lieblingsansatz ist normalerweise, einfach ein neues Diktat zu erstellen:

# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)

67
2018-03-22 00:04



Sie können eine Sammlung nicht ändern, während Sie sie durchlaufen. Auf diese Weise liegt der Wahnsinn - vor allem, wenn Sie das aktuelle Element löschen und löschen könnten, müsste der Iterator weitergehen (+1) und der nächste Aufruf an next würde dich darüber hinaus bringen (+2), so dass du am Ende ein Element überspringst (das direkt hinter dem, das du gelöscht hast). Sie haben zwei Möglichkeiten:

  • Kopieren Sie alle Schlüssel (oder Werte oder beides, je nachdem, was Sie benötigen), und durchlaufen Sie diese. Sie können verwenden .keys() Dazu geben Sie in Python 3 den resultierenden Iterator an list). Könnte jedoch im Weltraum sehr verschwenderisch sein.
  • Iterate über mydict Wie üblich, Speichern der zu löschenden Schlüssel in einer separaten Sammlung to_delete. Wenn Sie mit der Iteration fertig sind mydict, lösche alle Gegenstände in to_delete von mydict. Speichert einige (abhängig davon, wie viele Schlüssel gelöscht werden und wie viele bleiben) Speicherplatz über den ersten Ansatz, benötigt aber auch ein paar Zeilen mehr.

18
2018-03-21 23:28



Iterieren Sie stattdessen eine Kopie, z. B. die von Ihnen zurückgegebene items():

for k, v in list(mydict.items()):

13
2018-03-21 23:25



Mit python3 löst iterate on dic.keys () den Dictionary-Größenfehler aus. Sie können diesen alternativen Weg verwenden:

Getestet mit Python3, es funktioniert gut und der Fehler "Wörterbuch änderte die Größe während der Iteration"wird nicht angesprochen:

my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )

# Iterate on the list:
for k in key_list:
    print(key_list)
    print(my_dic)
    del( my_dic[k] )


print( my_dic )
# {}

Ich benutze es, wenn ich von einem Wörterbuch, das viel Speicher verwendet, ein anderes Wörterbuch (mit der Modifikation des ersten) erstellen will, ohne "kopieren" zu müssen und den RAM zu überlasten.


6
2017-10-10 18:07



Sie könnten zuerst eine Liste der zu löschenden Schlüssel erstellen und dann über diese Liste iterieren und sie löschen.

dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
    if v%2 == 1:
        delete.append(k)
for i in delete:
    del dict[i]

4
2018-03-02 19:31



Sie können ein Wörterbuchverständnis verwenden.

d = {k:d[k] for k in d if d[k] != val}


4
2018-05-19 17:57



Es ist am saubersten zu benutzen list(mydict):

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
...     if k == 'three':
...         del mydict[k]
... 
>>> mydict
{'four': 4, 'two': 2, 'one': 1}

Dies entspricht einer parallelen Struktur für Listen:

>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
... 
>>> mylist
['one', 'two', 'four']

Beide funktionieren in python2 und python3.


1
2018-01-20 15:25



Ich habe die obigen Lösungen in Python3 ausprobiert, aber diese scheint die einzige zu sein, die für mich beim Speichern von Objekten in einem Diktat funktioniert. Grundsätzlich erstellen Sie eine Kopie Ihres dict () und iterieren darüber, während Sie die Einträge in Ihrem ursprünglichen Wörterbuch löschen.

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])

0
2018-04-05 14:23