it-swarm.com.de

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 Sie darüber iterieren?

Zum Beispiel:

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

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

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

258
user248237

EDIT:

Diese Antwort funktioniert nicht für Python3 und gibt ein RuntimeError aus.

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

Dies geschieht, weil mydict.keys() einen Iterator und keine Liste zurückgibt. Wie in den Kommentaren erwähnt, konvertieren Sie mydict.keys() mit list(mydict.keys()) in eine Liste, und es sollte funktionieren.


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 angegeben, führt das Löschen von Einträgen zu Problemen, wenn der Iterator versucht, zum nächsten Eintrag überzugehen. Verwenden Sie stattdessen die keys() -Methode, um eine Liste der Schlüssel abzurufen 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 Elementwert löschen müssen, verwenden Sie stattdessen die items() -Methode:

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

Sie können dies 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 machen:

# 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)
82
Jochen Ritzel

Sie können eine Sammlung nicht ändern, während Sie sie iterieren. Auf diese Weise liegt 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 von next würde Sie darüber hinaus führen (+2) ), so dass Sie am Ende ein Element überspringen (das direkt hinter dem von Ihnen gelöschten). Sie haben zwei Möglichkeiten:

  • Kopieren Sie alle Schlüssel (oder Werte oder beides, je nachdem, was Sie benötigen) und wiederholen Sie diese. Sie können hierfür unter anderem .keys() verwenden (übergeben Sie in Python 3 den resultierenden Iterator an list).
  • Durchlaufen Sie mydict wie gewohnt und speichern Sie die zu löschenden Schlüssel in einer separaten Sammlung to_delete. Wenn Sie mit dem Durchlaufen von mydict fertig sind, löschen Sie alle Elemente in to_delete von mydict. Spart etwas Platz (abhängig von der Anzahl der gelöschten Schlüssel und der Anzahl der verbleibenden Schlüssel) beim ersten Versuch, benötigt aber auch ein paar Zeilen mehr.
18
user395760

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

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

Wenn Sie in python3 auf dic.keys () iterieren, wird der Wörterbuchgrößenfehler ausgelöst. Sie können diese alternative Methode verwenden:

Getestet mit python3, funktioniert es einwandfrei und der Fehler "Wörterbuch hat seine Größe während der Iteration geändert" wird nicht ausgegeben:

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 verwende es, wenn ich aus einem Wörterbuch mit viel Speicher ein anderes Wörterbuch erstellen möchte (das eine Modifikation des ersten enthält), ohne "zu kopieren" und den Arbeitsspeicher zu überlasten.

8
glihm

Es ist am saubersten, list(mydict) zu verwenden:

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

8
rsanden

Sie können ein Wörterbuch-Verständnis verwenden.

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

5
Aaron

Sie können zuerst eine Liste der zu löschenden Schlüssel erstellen und diese Liste dann durchlaufen, um sie zu 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
Pob

Es gibt eine Möglichkeit, die geeignet sein kann, wenn sich die zu löschenden Elemente immer am "Anfang" der Diktatur befinden

while mydict:
    key, value = next(iter(mydict.items()))
    if should_delete(key, value):
       del mydict[key]
    else:
       break

Der "Anfang" ist garantiert nur für bestimmte Python Versionen/Implementierungen. Zum Beispiel von What’s New In Python 3.7

die Beibehaltung der Einfügereihenfolge von Diktatobjekten wurde als offizieller Bestandteil der Python Sprachspezifikation erklärt.

Auf diese Weise wird eine Kopie des Diktats vermieden, die in vielen anderen Antworten vorgeschlagen wird, zumindest in Python 3.

3
Michal Charemza

Ich habe die obigen Lösungen in Python3 ausprobiert, aber dies scheint die einzige zu sein, die für mich funktioniert, wenn Objekte in einem Diktat gespeichert werden. Grundsätzlich machen Sie eine Kopie Ihres Diktats () 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])
1
JLandbrug