140 votes

Modifier un dicton Python tout en le parcourant

Supposons que nous ayons un dictionnaire Python d et nous itérons dessus comme suit :

for k, v in d.iteritems():
    del d[f(k)] # remove some item
    d[g(k)] = v # add a new item

( f et g ne sont que des transformations de boîte noire).

En d'autres termes, nous essayons d'ajouter/supprimer des éléments à d tout en le parcourant à l'aide de iteritems .

Est-ce bien défini ? Pourriez-vous fournir des références pour étayer votre réponse ?


Voir aussi <a href="https://stackoverflow.com/questions/11941817">Comment éviter l'erreur "RuntimeError : dictionary changed size during iteration" ? </a>pour la question distincte de savoir comment éviter le problème.

97voto

unutbu Points 222216

Alex Martelli s'exprime sur le sujet aquí .

Il peut être dangereux de changer de conteneur (par exemple, dict) tout en parcourant le conteneur en boucle. Ainsi, il n'est pas possible de del d[f(k)] peuvent ne pas être sûres. Comme vous le savez, la solution de contournement consiste à utiliser d.copy().items() (pour boucler sur une copie indépendante du conteneur) au lieu de d.iteritems() o d.items() (qui utilisent le même conteneur sous-jacent).

Il est possible de modifier la valeur d'un existant du dict, mais l'insertion de valeurs à de nouveaux indices (par ex. d[g(k)] = v ) peut ne pas fonctionner.

69voto

Raphaël Saint-Pierre Points 1439

Il est explicitement mentionné sur la page de documentation de Python (pour Python 2.7 ) que

Utilisation iteritems() tandis que l'ajout ou la suppression d'entrées dans le dictionnaire peut soulever un problème de sécurité. RuntimeError ou ne parvient pas à itérer sur toutes les entrées.

De même pour Python 3 .

Il en va de même pour iter(d) , d.iterkeys() et d.itervalues() et j'irai même jusqu'à dire que c'est le cas pour les for k, v in d.items(): (Je ne me souviens pas exactement de ce que for le fait, mais je ne serais pas surpris que l'implémentation appelée iter(d) ).

38voto

murgatroid99 Points 5099

Vous ne pouvez pas faire cela, du moins avec d.iteritems() . J'ai essayé, et Python échoue avec

RuntimeError: dictionary changed size during iteration

Si vous utilisez plutôt d.items() et cela fonctionne.

Dans Python 3, d.items() est une vue du dictionnaire, comme d.iteritems() dans Python 2. Pour faire cela en Python 3, il faut plutôt utiliser d.copy().items() . Cela nous permettra également d'itérer sur une copie du dictionnaire afin d'éviter de modifier la structure de données sur laquelle nous itérons.

18voto

2cynykyl Points 342

J'ai un grand dictionnaire contenant des tableaux Numpy, donc le dict.copy().keys() suggéré par @murgatroid99 n'était pas faisable (bien qu'il ait fonctionné). A la place, j'ai simplement converti le keys_view en une liste et cela a fonctionné correctement (en Python 3.4) :

for item in list(dict_d.keys()):
    temp = dict_d.pop(item)
    dict_d['some_key'] = 1  # Some value

Je me rends compte que cela ne plonge pas dans le domaine philosophique du fonctionnement interne de Python comme les réponses ci-dessus, mais cela fournit une solution pratique au problème énoncé.

6voto

combatdave Points 580

Le code suivant montre que ce n'est pas bien défini :

def f(x):
    return x

def g(x):
    return x+1

def h(x):
    return x+10

try:
    d = {1:"a", 2:"b", 3:"c"}
    for k, v in d.iteritems():
        del d[f(k)]
        d[g(k)] = v+"x"
    print d
except Exception as e:
    print "Exception:", e

try:
    d = {1:"a", 2:"b", 3:"c"}
    for k, v in d.iteritems():
        del d[f(k)]
        d[h(k)] = v+"x"
    print d
except Exception as e:
    print "Exception:", e

Le premier exemple appelle g(k) et lève une exception (le dictionnaire a changé de taille pendant l'itération).

Le deuxième exemple appelle h(k) et ne lève pas d'exception, mais affiche :

{21: 'axx', 22: 'bxx', 23: 'cxx'}

Ce qui, en regardant le code, semble erroné - j'aurais attendu quelque chose comme :

{11: 'ax', 12: 'bx', 13: 'cx'}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X