Joyitas en la stdlib: collections

Dentro de la biblioteca estándar de Python dispones de auténticas joyas, muchas veces ignoradas u olvidadas. Es por ello que voy a empezar un breve pero intenso recorrido por algunas piezas de arte disponibles de serie.

Módulo collections

Con la ayuda de este módulo puedes aumentar las estructuras de datos típicas disponibles en Python (listas, tuplas, diccionarios,…). Veamos algunas utilidades disponibles:

ChainMap

Solo Python 3. Actualízate!!
Dicho en bruto, es un conglomerado de diccionarios (también conocidos como mappings o hash tables).
Para que puede ser útil:

Ejemplo, imaginemos que tenemos un diccionario de configuración dict_a, que posee las claves a y b, y queremos actualizar sus valores con otros pares clave:valor que están en el diccionario dict_b, que posee las claves b y c. Podemos hacer:

Cuya salida será:

Hemos añadido el valor de la clave c de dict_b sin necesidad de modificar nuestro diccionario original de configuración dict_a, es decir, hemos hecho un ‘cambio’ reversible. También podemos ‘sobreescribir’ las claves que están en nuestro diccionario original de configuración, dict_b variando los parámetros del constructor:

Cuya salida será:

Vemos que, además de añadir la clave c, hemos sobreescrito la clave b.
Los diccionarios originales están disponibles haciendo uso del atributo maps:

Ejercicio: haced un dir de cm y un dir de dict_a y veréis que los atributos y métodos disponibles son parecidos.
Más información en este hilo de stackoverflow en el que me he basado para el ejemplo anterior (¿basar y copiar no son sinónimos?).

Counter

Permite contar ocurrencias de forma simple. En realidad, su funcionalidad se podría conseguir sin problemas con algunas líneas extra de código pero ya que lo tenemos, está testeado e implementado por gente experta vamos a aprovecharnos de ello.
En la documentación oficial hay algunos ejemplos interesantes y en github podéis encontrar unos cuantos más. Veamos un ejemplo simple pero potente, yo trabajo mucho con datos meteorológicos y uno de los problemas recurrentes es tener fechas repetidas que no deberían existir (pero pasa demasiado a menudo). Una forma rápida de buscar problemas de estos en ficheros y lanzar una alarma cuando ocurra lo que buscamos, sería:

Cuya salida será:

namedtuple

A veces me toca crear algún tipo de estructura que guarda datos y algunos metadatos. Una forma simple sin crear una clase ad-hoc sería usar un diccionario. Un ejemplo simple sería:

Cuya salida será:

Lo anterior es simple y rápido pero usando una namedtuple dispongo de algo parecido con algunas cosas extra. Veamos un ejemplo similar usando namedtuple:

Cuya salida será:

Ventajas que le veo con respecto a lo anterior:

  • Puedo acceder a los ‘campos’ o claves del diccionario usando dot notation

Cuya salida será:

  • Puedo ver el código usado para crear la estructura de datos usando verbose = True. Usa exec entre bambalinas (o_O).
  • Puedo ver que todas las claves se transforman en property‘s. Puedo ver que se crea documentación… MAGIA en estado puro!!!

(Si no quieres usar la keyword verbose = True puedes seguir teniendo acceso en un objeto usando obj._source)

  • Puedo seguir obteniendo un diccionario (un OrderedDict, también incluido en el módulo collections) si así lo deseo:
  • Puedo crear subclases de forma simple para añadir funcionalidad. Por ejemplo, creamos una nueva clase con un nuevo método que calcula la media de los valores:

Cuya salida será:

WOW!!!!!
Los ejemplos en la documentación oficial son muy potentes y dan nuevas ideas de potenciales usos.

deque

Otra joyita que quizá debería usar más a menudo sería deque. Es una secuencia mutable (parecido a una lista), pero con una serie de ventajas. Es una cola/lista cuyo principio y fin es ‘indistinguible’, es thread-safe y está diseñada para poder insertar y eliminar de forma rápida en ambos extremos de la cola (ahora veremos qué significa todo esto). Un uso evidente es el de usar, por ejemplo, una secuencia como stream de datos con un número de elementos fijo y/o rápidamente actualizable:

  • Podemos limitar su tamaño y si añadimos elementos por un lado se eliminan los del otro extremo.
  • Podemos rotar los datos de forma eficiente.

Veamos un ejemplo:

Cuya salida sería:

Cuya salida sería:

Veamos la eficiencia de esta operación:

Con una queue podemos anexar de forma eficiente a ambos lados:

Cuya salida será:

Cuya salida será:

Etc.
Puedes hacer cosas similares a las hechas con listas pero de forma más eficiente y práctica en determinados casos!!
Recordad que, además, disponemos del módulo queue en la librería estándar.

Conclusión

Este módulo esconde cosas muy interesantes, algunas que no hemos visto. Por tanto, si no lo conocéis, deberíais explorar el módulo collections, si lo conocéis nos podéis indicar como lo usáis en los comentarios que puedes encontrar más abajo.

3 comentarios en «Joyitas en la stdlib: collections»

  1. Estupendo artículo, como siempre. Vale la pena revisar la librería estándar de vez en cuando para descubrir joyas como éstas. Concretamente la deque creo que es de lo mejor que tenemos en nuestro arsenal. Su principal virtud es que se puede “insertar” datos por un extremo y “extraer” por el otro (y viceversa), que unido a que es “thread-safe” la convierte en el medio ideal para comunicar hilos de ejecución entre sí. ¡Nunca más uséis variables globales para conectar hilos, por favor!
    Pero ya que preguntas por los “usos” que les damos, un uso que me gusta mucho de “deque” es el extraer los últimos elementos de un iterador. De un iterador no se puede saber a priori cuántos elementos va a generar, por lo que si necesitamos los “n últimos elementos” no quedaría otra que extraer todos los elementos a una lista, con el consiguiente gasto de recursos que supone. El ‘islice()’ de ‘itertools’ sólo permite fragmentos del iterador contados desde el inicio (eg: no podemos hacer islice(iterador, -n) para obtener el final del iterador). Pero con deque podemos crear un contenedor de tamaño fijo que descarte todos los elementos del iterador para que nos quede los n elementos deseados. Algo así: list(deque(iterador, n)) . En la documentación del módulo se pone como “receta” el uso de esta técnica para leer las últimas líneas de un fichero.

    1. Leerte siempre es un placer ya que siempre se aprende algo. Lo de conectar hilos tienes razón!!
      Tenemos en cola más ideas que comentar sobre la librería estándar, si te animas a escribir algo siempre es bienvenido 🙂

  2. Pingback: Joyitas en la stdlib: collections | Python-es |...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

seven + one =