Itération sur dictionnaires utilisant des boucles "pour"
je suis un peu perplexe par le code suivant:
d = {'x': 1, 'y': 2, 'z': 3}
for key in d:
print key, 'corresponds to', d[key]
ce que je ne comprends pas c'est la partie key
. Comment Python reconnaître qu'il n'a besoin que de lire les clés du dictionnaire? Est-ce que key
est un mot spécial en Python? Ou est-ce simplement une variable?
11 réponses
key
est juste un nom de variable.
for key in d:
en boucle sur les touches dans le dictionnaire, plutôt que les clés et les valeurs. Pour passer en boucle à la fois la clé et la valeur, vous pouvez utiliser ce qui suit:
Pour Python 2.x:
for key, value in d.iteritems():
Pour Python 3.x:
for key, value in d.items():
pour tester par vous-même, changez le mot key
en poop
.
pour Python 3.x, iteritems()
a été remplacé par simplement items()
, qui renvoie une vue en set-like soutenue par le dict, comme iteritems()
mais encore mieux.
Ceci est également disponible en 2.7 comme viewitems()
.
L'opération items()
fonctionne pour les 2 et 3, mais dans le 2 il sera de retour une liste du dictionnaire (key, value)
paires, ce qui ne sera pas refléter les changements apportés à la dict que se passera après la items()
appel. Si vous voulez le 2.x comportement en 3.x, vous pouvez appelez list(d.items())
.
ce n'est pas que la clé est un mot spécial, mais que les dictionnaires implémentent le protocole iterator. Vous pouvez le faire dans votre classe, par exemple voir cette question pour savoir comment construire des itérateurs de classe.
dans le cas des dictionnaires, il est mis en œuvre au niveau C. Les détails sont disponibles dans PEP 234 . En particulier, la section intitulée "Dictionary Iterators":
- Les dictionnaires
implémentent un slot tp_iter qui retourne un itérateur qui itère sur les touches du dictionnaire. [...] Ce signifie que nous pouvons écrire
for k in dict: ...
qui est équivalent à, mais beaucoup plus rapide que
for k in dict.keys(): ...
aussi longtemps que la restriction sur les modifications au dictionnaire (soit par la boucle ou par un autre thread) ne sont pas violés.
ajouter méthodes de dictionnaires qui renvoient différents types de itérateurs explicitement:
for key in dict.iterkeys(): ... for value in dict.itervalues(): ... for key, value in dict.iteritems(): ...
signifie que
for x in dict
signifiefor x in dict.iterkeys()
.
itérant sur un dict
itère à travers ses clés dans aucun ordre particulier, comme vous pouvez le voir ici:
Edit: (C'est n'est plus le cas dans Python3.6 , mais note que c'est pas de garantie comportement encore)
>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> list(d)
['y', 'x', 'z']
>>> d.keys()
['y', 'x', 'z']
Pour ton exemple, c'est une meilleure idée d'utiliser dict.items()
:
>>> d.items()
[('y', 2), ('x', 1), ('z', 3)]
Cela vous donne une liste de tuples. Lorsque vous bouclez au-dessus d'eux comme ceci, chaque tuple est déballé dans k
et v
automatiquement:
for k,v in d.items():
print(k, 'corresponds to', v)
en utilisant k
et v
comme noms variables lors d'une boucle au-dessus d'un dict
est assez commun si le corps de la boucle est seulement quelques lignes. Pour les boucles plus compliquées, il peut être judicieux d'utiliser des noms plus descriptifs:
for letter, number in d.items():
print(letter, 'corresponds to', number)
C'est une bonne idée de prendre l'habitude d'utiliser le format cordes:
for letter, number in d.items():
print('{0} corresponds to {1}'.format(letter, number))
key
est simplement une variable.
Pour Python2.X :
d = {'x': 1, 'y': 2, 'z': 3}
for my_var in d:
print my_var, 'corresponds to', d[my_var]
... ou mieux,
d = {'x': 1, 'y': 2, 'z': 3}
for the_key, the_value in d.iteritems():
print the_key, 'corresponds to', the_value
Pour Python3.X :
d = {'x': 1, 'y': 2, 'z': 3}
for the_key, the_value in d.items():
print(the_key, 'corresponds to', the_value)
lorsque vous itérez les dictionnaires en utilisant la syntaxe for .. in ..
, il itère toujours sur les touches (les valeurs sont accessibles en utilisant dictionary[key]
).
pour itérer sur des paires de valeurs clés, utilisez for k,v in s.iteritems()
.
c'est un idiome très courant. in
est un opérateur. Pour savoir quand utiliser for key in dict
et quand il doit être for key in dict.keys()
voir article de Python idiomatique de David Goodger .
vous pouvez utiliser ceci:
for key,val in d.items():
print key, 'is the key for ', val
j'ai un cas d'utilisation où je dois itérer à travers le dict pour obtenir la clé, paire de valeur, aussi l'indice indiquant où je suis. C'est comme ça que je le fais:
d = {'x': 1, 'y': 2, 'z': 3}
for i, (key, value) in enumerate(d.items()):
print(i, key, value)
notez que les parenthèses autour de la clé, valeur est important, sans les parenthèses, vous obtenez une Valeurerror "pas assez de valeurs à déballer".
Itération sur les dictionnaires de l'aide "pour" boucles
d = {'x': 1, 'y': 2, 'z': 3} for key in d: ...
comment Python reconnaît-il qu'il n'a besoin que de lire la clé dictionnaire? Est-ce que key est un mot spécial en Python? Ou est-ce simplement un variable?
ce n'est pas juste for
boucles. Le mot important ici est "itération".
un dictionnaire est un mapping de clés à des valeurs:
d = {'x': 1, 'y': 2, 'z': 3}
chaque fois que nous itérons sur elle, nous itérons sur les clés. Le nom de la variable key
se veut seulement descriptif - et il est tout à fait approprié à cette fin.
cela se produit dans une liste de compréhension:
>>> [k for k in d]
['x', 'y', 'z']
il arrive quand nous passons le dictionnaire à la liste (ou tout autre objet de type collection):
>>> list(d)
['x', 'y', 'z']
La façon Python itère est, dans un contexte où il doit, il appelle la __iter__
méthode de l'objet (dans ce cas, le dictionnaire) qui renvoie un itérateur (dans ce cas, un keyiterator objet):
>>> d.__iter__()
<dict_keyiterator object at 0x7fb1747bee08>
nous ne devrions pas utiliser ces méthodes spéciales nous-mêmes, à la place, utiliser la fonction intégrée respective pour l'appeler, iter
:
>>> key_iterator = iter(d)
>>> key_iterator
<dict_keyiterator object at 0x7fb172fa9188>
itérateurs ont une __next__
méthode - mais nous l'appelons avec la fonction intégrée, next
:
>>> next(key_iterator)
'x'
>>> next(key_iterator)
'y'
>>> next(key_iterator)
'z'
>>> next(key_iterator)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Lorsqu'un itérateur est épuisé, il soulève StopIteration
. C'est ainsi que Python sait sortir d'une boucle for
, ou d'une compréhension de liste, ou d'une expression de générateur, ou de tout autre contexte itératif. Une fois qu'un itérateur soulève StopIteration
, il le soulève toujours - si vous voulez itérer à nouveau, vous avez besoin d'un nouveau.
>>> list(key_iterator)
[]
>>> new_key_iterator = iter(d)
>>> list(new_key_iterator)
['x', 'y', 'z']
Retour à dicts
nous avons vu des dicts itérer dans de nombreux contextes. Ce que nous avons vu, c'est que chaque fois que nous itérons sur un dict, nous avons la remise des clés. Retour à l'exemple original:
d = {'x': 1, 'y': 2, 'z': 3} for key in d:
si nous changeons le nom de la variable, nous obtenons toujours les clés. Essayons:
>>> for each_key in d:
... print(each_key, '=>', d[each_key])
...
x => 1
y => 2
z => 3
si nous voulons itérer sur les valeurs, nous devons utiliser la méthode .values
de dicts, ou pour les deux ensemble, .items
:
>>> list(d.values())
[1, 2, 3]
>>> list(d.items())
[('x', 1), ('y', 2), ('z', 3)]
Dans l'exemple donné, il serait plus efficace d'itérer sur les éléments comme ceci:
for a_key, corresponding_value in d.items():
print(a_key, corresponding_value)
mais à des fins académiques, l'exemple de la question est très bien.
vous pouvez vérifier l'implémentation du dicttype
de CPython sur GitHub. C'est la signature de la méthode qui implémente l'itérateur dict:
_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
PyObject **pvalue, Py_hash_t *phash)
pour itérer sur les touches, il est plus lent mais préférable d'utiliser my_dict.keys()
. Si vous avez essayé de faire quelque chose comme ceci:
for key in my_dict:
my_dict[key+"-1"] = my_dict[key]-1
cela créerait une erreur d'exécution parce que vous changez les clés pendant que le programme est en cours d'exécution. Si vous êtes absolument fixé sur la réduction, utilisez le for key in my_dict
, mais vous avez été averti ;).