La modification d'un Python dict lors de l'itération sur elle
Disons que nous avons un dictionnaire Python d
, et nous sommes une itération sur elle comme:
for k,v in d.iteritems():
del d[f(k)] # remove some item
d[g(k)] = v # add a new item
(f
et g
sont juste quelques-noir-boîte de transformations.)
En d'autres termes, nous essayons d'ajouter/supprimer des éléments à d
lors de l'itération sur l'aide de iteritems
.
Est-ce bien défini? Pourriez-vous donner quelques références à l'appui de votre réponse?
(Il est assez évident de comment résoudre ce problème si c'est cassé, donc ce n'est pas l'angle que je suis après.)
- double possible de python - pourquoi n'est-il pas sûr de modifier la séquence de itérée sur?
- Voir stackoverflow.com/questions/5384914/...
- J'ai essayé de le faire et il semble que si vous laissez initiale dict taille inchangée - par exemple, de remplacer tout de clé/valeur au lieu de les enlever ensuite ce code ne sera pas jeter l'exception
- Je suis en désaccord que c'est “assez évident comment résoudre ce problème si c'est cassé” pour tout le monde à la recherche de ce thème (dont moi-même), et je souhaite que l'on a accepté la réponse a au moins touché.
Vous devez vous connecter pour publier un commentaire.
Il est explicitement mentionné sur le Python page de doc (pour Python 2.7) que
De même pour Python 3.
La même chose vaut pour
iter(d)
,d.iterkeys()
etd.itervalues()
, et je vais aller aussi loin que de dire qu'il ne l'est pour lefor k, v in d.items():
(je ne me souviens pas exactement ce quefor
fait, mais je ne serais pas surpris si la mise en œuvre appeléeiter(d)
).d.items()
devriez être en sécurité dans Python 2.7 (le jeu change avec Python 3), car il rend ce qui est essentiellement une copie ded
, de sorte que vous n'êtes pas modifier ce que vous avez à parcourir.viewitems()
Alex Martelli pèse sur cette ici.
Il peut ne pas être sûr de changer l'emballage (dict) en bouclant sur le conteneur.
Donc
del d[f(k)]
peut-être pas sûr. Comme vous le savez, la solution de contournement consiste à utiliserd.items()
(faire une boucle sur une copie indépendante du conteneur) au lieu ded.iteritems()
(qui utilise le même conteneur sous-jacent).Il est d'accord pour modifier la valeur à un existant indice de la dict, mais l'insertion de valeurs à de nouveaux indices (par exemple,
d[g(k)]=v
) peuvent ne pas fonctionner.Vous ne pouvez pas le faire, au moins avec
d.iteritems()
. Je l'ai essayé, et Python échoue avecSi vous utilisez à la place
d.items()
, puis il travaille.En Python 3,
d.items()
est vue dans le dictionnaire, commed.iteritems()
en Python 2. Pour ce faire, dans Python 3, au lieu d'utiliserd.copy().items()
. Cela va de même de nous permettre de parcourir une copie du dictionnaire afin d'éviter la modification de la structure de données, nous sommes à parcourir.2to3
) de Py2 ded.items()
à Py3 estlist(d.items())
, bien qued.copy().items()
est probablement de même de l'efficacité.Le code suivant montre que ce n'est pas bien définie:
Le premier exemple des appels de g(k), et lève une exception (dictionnaire changé de taille au cours de l'itération).
Le deuxième exemple d'appels h(k) et jette pas une exception, mais les sorties:
Qui, en regardant le code, semble mal - je me serais attendu à quelque chose comme:
{11: 'ax', 12: 'bx', 13: 'cx'}
mais le 21,22,23 devrait vous donner une idée de ce qui s'est réellement passé: la boucle est allé par le biais d'éléments 1, 2, 3, 11, 12, 13 mais n'ai pas réussi à récupérer le second tour des nouveaux éléments qu'ils sont insérés avant les éléments que vous avait déjà itéré. Changementh()
de retourx+5
et vous obtenez un autre x:'axxx'
etc. ou "x+3' et vous obtenez le magnifique'axxxxx'
{11: 'ax', 12: 'bx', 13: 'cx'}
comme vous l'avez dit, donc je vais mettre à jour mon post à ce sujet. De toute façon, ce n'est clairement pas bien défini comportement.J'ai un gros dictionnaire contenant les tableaux Numpy, de sorte que le dict.copier().les touches() chose suggéré par @murgatroid99 n'était pas possible (si elle a fonctionné). Au lieu de cela, j'ai juste converti le keys_view à une liste et il a bien fonctionné (en Python 3.4):
Je me rends compte ce n'est pas plongée dans le domaine philosophique de Python fonctionnement interne que les réponses ci-dessus, mais il offre une solution pratique au problème posé.
J'ai eu le même problème et j'ai utilisé la procédure suivante pour résoudre ce problème.
Liste Python peut être itérer même si vous modifiez lors de l'itération sur elle.
donc, pour la suite de code, il va imprimer de 1 à l'infini.
Donc à l'aide de la liste et dict en collaboration, vous pouvez résoudre ce problème.
Aujourd'hui, j'ai eu ce genre de cas d'utilisation, mais au lieu de simplement se matérialiser les touches sur le dictionnaire au début de la boucle, je voulais des modifications à la dict affecter l'itération de la dict, qui a été ordonnée, dict.
J'ai fini la construction de la routine suivante, qui peut également être trouvé dans jaraco.itertools:
La docstring montre l'utilisation. Cette fonction peut être utilisée à la place de
d.iteritems()
ci-dessus pour avoir l'effet désiré.Python 3, vous devez juste:
ou de l'utilisation:
Vous ne devriez jamais modifier dictionnaire original, il conduit à la confusion ainsi que les éventuels bugs ou RunTimeErrors. Sauf si vous venez d'ajouter au dictionnaire avec de nouveaux noms de clé.