Comment supprimer des éléments d'un dictionnaire tout en itérant dessus?

Est-il légitime de supprimer des éléments d'un dictionnaire en Python tout en itérant dessus?

Par exemple:

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

L'idée est de supprimer des éléments qui ne répondent pas à une certaine condition du dictionnaire, au lieu de créer un nouveau dictionnaire qui est un sous-ensemble de celui qui est itéré.

Est-ce une bonne solution? Il y a plus élégant/efficaces?

211
demandé sur Trilarion 2011-03-22 02:23:29

9 réponses

Un test simple dans la console montre que vous ne pouvez pas modifier un dictionnaire en itérant dessus:

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

Comme indiqué dans la réponse de delnan, la suppression d'entrées provoque des problèmes lorsque l'itérateur essaie de passer à l'entrée suivante. Au lieu de cela, utilisez la méthode keys() pour obtenir une liste des clés et travailler avec cela:

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

Si vous devez supprimer en fonction de la valeur des éléments, Utilisez la méthode items() à la place:

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

Modifier:

Ci-dessus ne fonctionnera pas pour Python3 et donnez un RuntimeError.

RuntimeError: le dictionnaire a changé de taille lors de l'itération.

Cela se produit parce que mydict.keys() renvoie un itérateur pas une liste. Comme indiqué dans les commentaires, convertissez simplement mydict.keys() en une liste par list(mydict.keys()) et cela devrait fonctionner.

247
répondu Blair 2018-04-25 12:53:51

Vous pouvez également le faire en deux étapes:

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

Mon approche préférée est généralement de simplement faire un nouveau dict:

# 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)
69
répondu Jochen Ritzel 2016-02-29 16:47:00

Vous ne pouvez pas modifier une collection en l'itérant. De cette façon se trouve la folie-plus particulièrement, si vous étiez autorisé à supprimer et à supprimer l'élément actuel, l'itérateur devrait passer à autre chose (+1) et le prochain appel à next Vous emmènerait au-delà (+2), de sorte que vous finiriez par sauter un élément (celui juste derrière celui que vous avez supprimé). Vous avez deux options:

  • copiez toutes les clés (ou valeurs, ou les deux, en fonction de ce dont vous avez besoin), puis itérez-les. Vous pouvez utiliser .keys() et al pour cela (en Python 3, passez l'itérateur résultant à list). Pourrait être très le gaspillage de l'espace-sage que.
  • itérer sur mydict comme d'habitude, en enregistrant les clés à supprimer dans une collection séparée to_delete. Lorsque vous avez terminé d'itérer mydict, supprimez tous les éléments de to_delete de mydict. Économise de l'espace (en fonction du nombre de clés supprimées et du nombre de séjours) sur la première approche, mais nécessite également quelques lignes supplémentaires.
18
répondu 2011-03-21 23:34:37

Itérer sur une copie à la place, comme celle renvoyée par items():

for k, v in list(mydict.items()):
13
répondu Ignacio Vazquez-Abrams 2017-09-30 19:58:44

Avec python3, itérer sur dic.keys() augmentera l'erreur de taille du dictionnaire. Vous pouvez utiliser cette alternative:

Testé avec python3, il fonctionne très bien et l'Erreur "dictionnaire changé de taille au cours d'une itération" n'est pas évoqué:

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

Je l'utilise quand, à partir d'un dictionnaire utilisant beaucoup de mémoire, je veux construire un autre dictionnaire (contenant la modification du premier) sans faire "copier" et surcharger la RAM.

6
répondu glihm 2017-02-22 15:05:35

Vous pouvez d'abord créer une liste de clés à supprimer, puis parcourir cette liste en les supprimant.

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
répondu Pob 2017-03-02 19:31:06

Vous pouvez utiliser une compréhension de dictionnaire.

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

4
répondu Aaron 2017-05-19 17:57:08

C'est le plus propre à utiliser 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}

Cela correspond à une structure parallèle pour les listes:

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

Les deux fonctionnent en python2 et python3.

1
répondu rsanden 2018-01-20 15:53:35

J'ai essayé les solutions ci-dessus en Python3 mais celle-ci semble être la seule à fonctionner pour moi lors du stockage d'objets dans un dict. Fondamentalement, vous faites une copie de votre dict () et itérez-le tout en supprimant les entrées de votre dictionnaire d'origine.

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])
0
répondu JLandbrug 2018-04-05 14:23:29