Recorrer diccionarios en Python

Alguna vez me han preguntado ¿Cuál es la manera más «pythónica» de recorrer diccionarios?

Mi primera reacción fue advertir que en raras ocasiones uno debe recorrer un diccionario. Tan sólo imaginen las razones por las cuales alguna vez han tenido que buscar página por página palabras en el Diccionario de la lengua española.

Los diccionarios en formato de libros se diseñaron precisamente para evitar esa actividad, y permiten, por medio de búsqueda binaria, acceder de manera casi directa a al término que se está buscando.

Pero si deben recorrer término a término un diccionario por alguna razón, Python ofrece varias maneras.

Recorrido implícito

Todos los tipos de datos «transitables» que incluye Python (listas, conjuntos, diccionarios, etc.) tienen una manera implícita de recorrerlos.

Cuando expresamos la intención de recorrer un objeto, Python elige el método implícito de recorrido correspondiente al objeto.

Construimos dicha expresión en Python combinando las palabras «for» e «in» de la siguiente manera:

for <variable> in <objeto>

En particular, la manera implícita de recorrer los diccionarios en Python consiste en observar cada una de sus «claves», una a la vez.

Veamos un ejemplo:

diccionario = {
  'clave 1': 'valor 1',
  'clave 2': 'valor 2',
  'clave 3': 'valor 3'
}
# Recorrer diccionario
# Por cada clave en el diccionario
for clave in diccionario:
    # Hacer algo con esa clave
    print(clave)
0.3s
Python

Si queremos hacer algo con los valores referidos por las claves, contamos con el mismísimo diccionario que recorremos para obtener dichos valores (para esto es que se hicieron los diccionarios).

for clave in diccionario:
  # Obtener el valor referido por clave
  valor = diccionario[clave] # <-- Así es que debería usarse un diccionario
                             #    ¿Por qué es que estás revisando término a
                             #     término el diccionario? ¯\_(ツ)_/¯
  # Hacer algo con ese valor
  print(valor)
0.3s
Python

Métodos especiales de recorrido

Además de la manera implícita, los tipos de datos «transitables» que incluye Python exponen métodos alternativos de recorrido.

En particular, los diccionarios ofrecen los métodos «keys», «values» e «items».

keys

«Keys» nos permite recorrer el diccionario observando cada clave a la vez. Éste método es, de hecho, el método implícito que ya discutimos.

for clave in diccionario.keys():
  print(clave)
0.3s
Python

Debido a lo redundante, para recorrer diccionarios clave a clave, no acostumbramos a indicar explícitamente este método.

¿por qué existe esta redundancia?

Habrá que ponerse en los zapatos de quien diseñó Python.

Yo imagino que después de crear el diccionario en el lenguaje, el diseñador definió los métodos con los que podemos recorrer los diccionarios («keys», «values», «items»). Por último, eligió a uno de esos métodos para ser el recorrido implícito.

Además, estos métodos no los usamos únicamente para recorrer diccionarios. Podemos usarlos como colecciones para otros propósitos. Ej. invocar alguna función con las claves como argumento pero no queremos exponer los valores del diccionario a esa función, e.j:

print(diccionario.keys()) # La salida no muestra los valores referidos
0.3s
Python

values

Si en vez de las claves, queremos observar los valores uno a uno, podemos usar el método «values».

for valor in diccionario.values():
  print(valor)
0.3s
Python

La ventaja de este estilo sobre utilizar el método implícito, es que las expresiones que conforman el procesamiento del objeto observado en cada repetición quedan más sucintas, más cercanas a la intención de «procesar valores».

Pero, dichas expresiones quedan entonces limitadas a sólo «procesar valores». Si por alguna razón necesitamos luego «procesar las claves» también, debemos cambiar el método del recorrido: una posición bien incómoda.

Elaboro...

Supongamos que nuestra intención es mostrar todos los valores del diccionario.

Supongamos que nuestra primera solución se basó en el recorrido implícito:

for clave in diccionario:
  print(diccionario[clave])
0.3s
Python

El texto print(diccionario[clave]), que se trata meramente de mostrar un valor, tiene el concepto del diccionario y el de una búsqueda en el mismo.

Si en cambio, utilizamos el método «values», ese texto sería mucho más simple; enfocado en solucionar su pequeño problema: mostrar algo por pantalla: print(algo).

for valor in diccionario.values():
  print(valor)
0.3s
Python

Muy elegante, pero demasiado ajustado.

Si en un futuro debemos mostrar ambas cosas (clave y valor), tendríamos que cambiar tanto el método de recorrido como el procesamiento del valor.

A continuación mostraré una solución que muestra ambas cosas.

De las 2 versiones anteriores, la que nos costaría menos cambiar para que se transforme en esta nueva solución sería la primera versión. Verifíquenlo:

for clave in diccionario:
  print(diccionario[clave])
  print(clave) # <- Partiendo de la primera versión
               #    éste es el único cambio.
0.3s
Python

Aprecien entonces que el método implícito es más flexible que el método «values», pero menos «atinado» si el propósito se limita a observar sólo los valores.

items

Si extrañan la elegancia y «puntería» de utilizar el método «values» en el contexto adecuado, sólo por no perder flexibilidad, entonces el método «items» es lo que necesitan.

Ofrece tanto flexibilidad como puntería y por eso es el método de recorrido que prefiero. Probablemente esté sacrificando algo de desempeño, pero si puedo pagar ese precio, lo hago. Y si no, pues no lo hago: simple.

Con este método visitamos tanto la clave como el valor cada vez. Entonces podemos hacer referencia a cada parte de forma independiente en el procesamiento.

for clave, valor in diccionario.items():
  print(valor)
  print(clave)
0.3s
Python

Para comparar mejor «items» con los métodos anteriores, repitamos el ejercicio de empezar con un programa que muestra sólo las claves, y luego debemos modificarlo para que muestre también los valores.

Pero esta vez, utilicemos el método «items» desde el principio.

La primera versión sería el programa que muestra sólo los valores:

for clave, valor in diccionario.items():
  print(valor)
0.3s
Python

Noten que el procesamiento está enfocado en sólo la tarea de mostrar algo por pantalla. No busca nada en ningún diccionario ni menciona su nombre.

Es decir, es print(valor) contra print(diccionario[clave]).

Sin embargo, noten ya la flexibilidad en el hecho que de print(diccionario[clave]) también funciona.

Esto se debe a que la expresión del recorrido esta preparada para cualquier escenario:

  • Observar cada clave

  • Observar cada valor

Para imprimir también la clave, el cambio resulta igual de atinado a lo que tenemos: añadimos print(clave).

for clave, valor in diccionario.items():
  print(valor)
  print(clave)
0.3s
Python

El método más «pythónico»

La primera reacción que tendrían para otorgar este título sería dárselo al método implícito. Y está bien, démosle ese título.

Pero a la hora de programar, no elijan un estilo porque tenga ese título.

  • Usen el sentido común

  • Entiendan un poco los pros y contras de cada estilo

  • Y lo más importante: Simplemente recorran el diccionario

Personalmente prefiero utilizar el método «items», siempre (aunque no recorra muchos diccionarios). Las razones, ya las expliqué, pero resumidamente son:

  • Flexibilidad

  • Atinamiento

Appendix

A la hora de colaborar en un mismo proyecto de software, los aspectos de estilo se vuelven más importantes.

Si todos los colaboradores se ponen de acuerdo y se esfuerzan en respetar lo convenido, a la larga el trabajo es más cómodo.

La guía de estilo más popular en la comunidad de Python es conocida como el PEP-8. Es una buena idea que lo lean si tienen intención de colaborar en el desarrollo dea algún proyecto con Python que cuente con más de una persona.

La guía de Python de Google también es muy popular y ésta menciona algunas cosas acerca de recorridos. Lo más interesante es que es considerado «buen estilo» por ellos utilizar o el método implícito o el método «items».

Runtimes (1)