Iterando sobre diccionarios usando bucles 'for'

3620

Estoy un poco desconcertado por el siguiente código:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    print (key, 'corresponds to', d[key])

Lo que no entiendo es la keyporción. ¿Cómo reconoce Python que solo necesita leer la clave del diccionario? ¿Es keyuna palabra especial en Python? ¿O es simplemente una variable?

3
  • 23
    Antes de publicar una nueva respuesta, considere que ya hay más de 10 respuestas para esta pregunta. Por favor, asegúrese de que su respuesta aporte información que no se encuentre entre las respuestas existentes. janniks 3 feb.20 a las 11:51
  • Como sabemos eso en Python Dictionary como un conjunto de pares clave: valor, con el requisito de que las claves sean únicas (dentro de un diccionario). Un par de llaves crea un diccionario vacío: {}. Al colocar una lista separada por comas de pares clave: valor entre llaves, se agregan pares clave: valor iniciales al diccionario. No, key no es una palabra especial en Python. Aquí la clave es solo un nombre de variable. Raksha Saini 10 de enero a las 4:26
  • intentaste for key, value in d.items()::? Charlie Parker 16 de marzo a las 22:11
6024

key es solo un nombre de variable.

for key in d:

simplemente recorrerá las claves en el diccionario, en lugar de las claves y los valores. Para recorrer tanto la clave como el valor, puede usar lo siguiente:

Para Python 3.x:

for key, value in d.items():

Para Python 2.x:

for key, value in d.iteritems():

Para probarlo usted mismo, cambie la palabra keya poop.

En Python 3.x, iteritems()se reemplazó con simplemente items(), que devuelve una vista similar a un conjunto respaldada por el dict, me gusta iteritems()pero aún mejor. Esto también está disponible en 2.7 como viewitems().

La operación items()funcionará tanto para 2 como para 3, pero en 2 devolverá una lista de los (key, value)pares del diccionario , que no reflejará los cambios en el dict que sucedan después de la items()llamada. Si desea el comportamiento 2.x en 3.x, puede llamar list(d.items()).

11
  • 206
    Agregar una razón pasada por alto para no acceder a un valor como este: d [clave] dentro del bucle for hace que la clave vuelva a ser hash (para obtener el valor). Cuando el diccionario es grande, este hash adicional se sumará al tiempo total. Esto se discute en la charla tecnológica de Raymond Hettinger youtube.com/watch?v=anrOzOapJ2Equiet_penguin 28/07/2017 a las 9:43
  • 36
    Podría tener sentido mencionar que los elementos se iterarán en un orden impredecible y sortedes necesario para estabilizarlos. yugr 25/08/18 a las 9:06
  • 6
    @HarisankarKrishnaSwamy ¿cuál es la alternativa? JoeyC 8 de noviembre de 2018 a las 4:45
  • 9
    @yugr ¿Por qué dices eso? Los documentos dicen Keys and values are iterated over in insertion order. [ docs.python.org/3/library/…Geza Turi 13/07/19 a las 15:48
  • 20
    @yugr Desde Python 3.7, los diccionarios están ordenados por inserción y esta es una característica del lenguaje. Ver stackoverflow.com/a/39980744/9428564Aimery 9/09/19 a las 14:54
489

No es que la clave sea una palabra especial, sino que los diccionarios implementan el protocolo de iterador. Puede hacer esto en su clase, por ejemplo, vea esta pregunta sobre cómo construir iteradores de clase.

En el caso de los diccionarios, se implementa en el nivel C. Los detalles están disponibles en PEP 234 . En particular, la sección titulada "Iteradores de diccionario":

  • Dictionaries implement a tp_iter slot that returns an efficient iterator that iterates over the keys of the dictionary. [...] This means that we can write

    for k in dict: ...
    

    which is equivalent to, but much faster than

    for k in dict.keys(): ...
    

    as long as the restriction on modifications to the dictionary (either by the loop or by another thread) are not violated.

  • Add methods to dictionaries that return different kinds of iterators explicitly:

    for key in dict.iterkeys(): ...
    
    for value in dict.itervalues(): ...
    
    for key, value in dict.iteritems(): ...
    

    This means that for x in dict is shorthand for for x in dict.iterkeys().

En Python 3, dict.iterkeys(), dict.itervalues()y dict.iteritems()ya no son compatibles. Utilice dict.keys(), dict.values()y en su dict.items()lugar.

0
239

Iterando sobre un dictitera a través de sus claves sin ningún orden en particular, como puede ver aquí:

(Este ya no es el caso en Python 3.6 , pero tenga en cuenta que todavía no es un comportamiento garantizado ).

>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> list(d)
['y', 'x', 'z']
>>> d.keys()
['y', 'x', 'z']

Para su ejemplo, es una mejor idea usar dict.items():

>>> d.items()
[('y', 2), ('x', 1), ('z', 3)]

Esto le da una lista de tuplas. Cuando los recorre de esta manera, cada tupla se desempaqueta ky vautomáticamente:

for k,v in d.items():
    print(k, 'corresponds to', v)

El uso de ky vcomo nombres de variable al recorrer un bucle dictes bastante común si el cuerpo del bucle tiene solo unas pocas líneas. Para bucles más complicados, puede ser una buena idea utilizar nombres más descriptivos:

for letter, number in d.items():
    print(letter, 'corresponds to', number)

Es una buena idea acostumbrarse a usar cadenas de formato:

for letter, number in d.items():
    print('{0} corresponds to {1}'.format(letter, number))
1
  • 20
    De las notas de la versión de Python 3.7: "La naturaleza de preservación del orden de inserción de los objetos dict es ahora una parte oficial de la especificación del lenguaje Python". Gregory Arenius 18 de julio de 2018 a las 16:30
102

key es simplemente una variable.

Para Python2.X :

d = {'x': 1, 'y': 2, 'z': 3} 
for my_var in d:
    print my_var, 'corresponds to', d[my_var]

... o mejor,

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.iteritems():
    print the_key, 'corresponds to', the_value

Para Python3.X :

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.items():
    print(the_key, 'corresponds to', the_value)
0
71

Cuando itera a través de diccionarios usando la for .. in ..-sintaxis, siempre itera sobre las claves (los valores son accesibles usando dictionary[key]).

Para iterar sobre pares clave-valor, en Python 2 use for k,v in s.iteritems()y en Python 3 for k,v in s.items().

1
  • 44
    Tenga en cuenta que para Python 3, es en items()lugar deiteritems()Andreas Fester 26/03/15 a las 11:38
39

Este es un modismo de bucle muy común. ines un operador. Para saber cuándo usar for key in dicty cuándo debe ser, for key in dict.keys()vea el artículo de Idiomatic Python de David Goodger (copia archivada) .

1
  • Mientras leo estas secciones in, la parte del operador es donde verificas la existencia . Quizás sea mejor eliminar esta in is an operatorinformación. Wolf 19 de mayo de 2016 a las 12:17
27

Tengo un caso de uso en el que tengo que iterar a través del dictado para obtener la clave, el par de valores, también el índice que indica dónde estoy. Así es como lo hago:

d = {'x': 1, 'y': 2, 'z': 3} 
for i, (key, value) in enumerate(d.items()):
   print(i, key, value)

Tenga en cuenta que los paréntesis alrededor de la clave, el valor son importantes, sin ellos, obtendría un ValueError"no hay suficientes valores para descomprimir".

2
  • 2
    ¿Cómo es esto relevante para la pregunta? jorijnsmit 13 abr.20 a las 9:19
  • No me gusta el uso de iaquí. Implica que los valores se repiten en un orden coherente, lo que no es el casoNeuron 25 de febrero a las 12:55
26

Iterating over dictionaries using 'for' loops

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    ...

How does Python recognize that it needs only to read the key from the dictionary? Is key a special word in Python? Or is it simply a variable?

No son solo forbucles. La palabra importante aquí es "iterar".

Un diccionario es un mapeo de claves a valores:

d = {'x': 1, 'y': 2, 'z': 3} 

Cada vez que iteramos sobre él, iteramos sobre las claves. El nombre de la variable keysolo pretende ser descriptivo, y es bastante adecuado para ese propósito.

Esto sucede en una lista de comprensión:

>>> [k for k in d]
['x', 'y', 'z']

Ocurre cuando pasamos el diccionario a list (o cualquier otro objeto de tipo colección):

>>> list(d)
['x', 'y', 'z']

La forma en que Python itera es, en un contexto donde lo necesita, llama al __iter__método del objeto (en este caso, el diccionario) que devuelve un iterador (en este caso, un objeto keyiterator):

>>> d.__iter__()
<dict_keyiterator object at 0x7fb1747bee08>

No deberíamos usar estos métodos especiales nosotros mismos, en su lugar, use la función incorporada respectiva para llamarlo iter:

>>> key_iterator = iter(d)
>>> key_iterator
<dict_keyiterator object at 0x7fb172fa9188>

Los iteradores tienen un __next__método, pero lo llamamos con la función incorporada 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

Cuando un iterador se agota, sube StopIteration. Así es como Python sabe salir de un forbucle, o una lista de comprensión, o una expresión generadora, o cualquier otro contexto iterativo. Una vez que un iterador aumenta StopIteration, siempre lo aumentará; si desea iterar nuevamente, necesita uno nuevo.

>>> list(key_iterator)
[]
>>> new_key_iterator = iter(d)
>>> list(new_key_iterator)
['x', 'y', 'z']

Volviendo a los dictados

Hemos visto dictados iterando en muchos contextos. Lo que hemos visto es que cada vez que iteramos sobre un dictado, obtenemos las claves. Volviendo al ejemplo original:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:

Si cambiamos el nombre de la variable, todavía obtenemos las claves. Vamos a intentarlo:

>>> for each_key in d:
...     print(each_key, '=>', d[each_key])
... 
x => 1
y => 2
z => 3

Si queremos iterar sobre los valores, necesitamos usar el .valuesmétodo de dictados, o para ambos juntos .items:

>>> list(d.values())
[1, 2, 3]
>>> list(d.items())
[('x', 1), ('y', 2), ('z', 3)]

En el ejemplo dado, sería más eficiente iterar sobre los elementos como este:

for a_key, corresponding_value in d.items():
    print(a_key, corresponding_value)

Pero para fines académicos, el ejemplo de la pregunta está bien.

11

Puede verificar la implementación de CPython dicttypeen GitHub. Esta es la firma del método que implementa el iterador dict:

_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
             PyObject **pvalue, Py_hash_t *phash)

CPython dictobject.c

8

Esto imprimirá la salida en orden ordenado por valores en orden ascendente.

d = {'x': 3, 'y': 1, 'z': 2}

def by_value(item):
    return item[1]

for key, value in sorted(d.items(), key=by_value):
    print(key, '->', value)

Producción:

y -> 1
z -> 2
x -> 3
1
  • 1
    No creo que esta fuera la pregunta. La pregunta era sobre la clave y por qué Python toma las claves del diccionario sin la opción .items () o .keys (). Joe Ferndz 29/12/20 a las 23:25
7

Para iterar sobre las claves, es más lento pero mejor de usar my_dict.keys(). Si intentó hacer algo como esto:

for key in my_dict:
    my_dict[key+"-1"] = my_dict[key]-1

crearía un error de tiempo de ejecución porque está cambiando las claves mientras el programa se está ejecutando. Si está absolutamente decidido a reducir el tiempo, use el for key in my_dictcamino, pero ha sido advertido.

1
  • 1
    ¿Por qué es "mejor" utilizar my_dict.keys()en lugar de iterar directamente sobre el diccionario? La iteración sobre un diccionario está claramente documentada como claves de rendimiento. Parece que tenía Python 2 en mente cuando respondido a esta, ya que en Python 3 for key in my_dict.keys()será todavía tiene el mismo problema con el cambio del tamaño del diccionario durante la iteración . Martijn Pieters 13/10/20 a las 14:28
5

Si busca un ejemplo claro y visual:

cat  = {'name': 'Snowy', 'color': 'White' ,'age': 14}
for key , value in cat.items():
   print(key, ': ', value)

Resultado:

name:  Snowy
color:  White
age:  14
0

Vayamos directo al grano. Si la palabra clave es solo una variable, como ha mencionado, lo principal a tener en cuenta es que cuando ejecuta un 'FOR LOOP' en un diccionario, se ejecuta solo a través de las 'claves' e ignora los 'valores' .

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    print (key, 'corresponds to', d[key])

mejor prueba esto:

d = {'x': 1, 'y': 2, 'z': 3} 
for i in d:
    print (i, 'corresponds to', d[i])

pero si usa una función como:

d = {'x': 1, 'y': 2, 'z': 3}
print(d.keys())

en el caso anterior, 'claves' simplemente no es una variable, es una función.