Uso de Pipes en Python



@eduardo_gpg

Número de visitas 2680

Tiempo de lectura 5 min

1 Noviembre 2021

Hace un par de post hablamos acerca de las funciones anónimas en Python y sobre la programación funcional con este lenguaje (Si aun no los haz leído te los recomiendo encarecidamente). Pues bien, para complementar dichos post, en esta ocasión me gustaría habláramos sobre una librería en Python que nos permitirá implementar pipes (|). Sí, pipes (|), los mismos que podemos utilizar en sistemas operativos unix. 😎

Si nunca habías escuchado hablar acerca de los pipes, no te preocupes, aquí te compartimos un post en donde te lo explicamos en detalle. Espero puedas echarla un vistazo.

Este será un post muy interesante, principalmente si te encuentras enfocado en dar tus primero pasos con la programación funcional en Python. Así que te invito a que te quedes.

Bien, una vez dicho todo esto, y sin más introducción, comencemos con el tutorial. 😁

Pipes

Lo primero que debemos saber es que, en Python no es posible implementar los pipes (|) de forma nativa, por lo tanto debemos hacer uso de la librería pipe. Esta librería nos permitirá ejecutar una serie de funciones de forma secuencial, tomando el output de una función como el input de otra.

Para poder hacer uso de la librería lo primero que debemos hacer será instarla.

pip install pipe

Una vez la libaría haya sido instalada, ahora sí, ya podemos hacer uso de pipes.

Nota: Es importante mencionar que Python posee el módulo pipes en su biblioteca estándar (En plural). Módulo que no debe ser confundido con la librería pipe que acabamos de instalar. 🤓

La librería pipe se apoya mucho del uso de funciones anónimas, lo cual nos viene estupendo si queremos implementar programación funcional. Veamos un par de ejemplos.

Comencemos con algo sencillo. Por ejemplo, obtengamos todos los números pares mayores a 10 que se encuentren dentro de un listado.

Utilizando la librería pipe, el código pudiera quedar de la siguiente manera.

>>> from pipe import where

>>> listado = [4, 6, 8, 10, 9, 11, 34, 12, 15, 16]
>>> resultado = listado | where(lambda x: x > 10)  | where(lambda x: x % 2 == 0)

>>> resultado
 <generator object where>

Es mediante la función where seremos capaces de filtrar elementos de una colección, vaya, condicionarlos. Basta con pasar como argumento la función lambda que deseamos ejecutar. 🐣

Para este ejemplo he definido funciones lambdas con un solo parámetro (x), el cual hace referencia a una elemento dentro de la colección. Teniendo en cuenta que cada función deberá ejecutarse por cada uno de los elementos en la colección.

La primera condición nos permite obtener todos aquellos elementos mayores a 10, y posteriormente, la segunda condición todos aquellos elementos pares. Condicionamos en 2 ocasiones. De forma secuencial. La salida de la primera función where es utilizada como la entrada para la segunda función where.

El resultado será un objeto de tipo generator. Objeto que fácilmente podemos convertir a un listado. utilizando la función list.

>>> list(resultado)
[34, 12, 16]

Que de hecho, también tenemos una serie de generadores en PyWombat. 🍻

En mi caso únicamente 3 elementos cumplen con la condición, ser mayores a 10 y ser números pares.

Como podemos observar, el uso de lambdas nos permite dividir el problema en partes pequeñas, pudiendo así construir funciones que realicen una y solo una tarea. Que quizás de forma individual no supongan gran cosa, pero en conjunto logran solventar la problemática.

Ok, ahora, quizás te estés preguntando ¿Qué no acaso la función where hace exactamente lo mismo que la función filter que Python ya posee? Y la respuesta es sí. Sin embargo creo que al hacer uso de la función Where el código se vuelve mucho más legible, y da pie al poder ejecutar múltiples funciones de forma secuencial, algo que con filter sería un poco complicado. 🙅‍♀️

Bien, y ¿Acaso existe un equivalente para la función map de Python con pipe? Y de nuevo la respuesta es sí. Veamos otro ejemplo.

Que te parece si ahora obtenemos el cuadrado de cada uno de los elementos dentro del listado. Para ello haremos uso de la función select, que sería el equivalente de la función map en Python.

>>> from pipe import select

>>> resultado = listado | select(lambda x: x ** 2)
>>> list(resultado)
[16, 36, 64, 100, 81, 121, 1156, 144, 225, 256]

Utilizando select podremos aplicar una función sobre cada uno de los elementos dentro de la colección. Obteniendo así una nueva colección con nuevos elementos.

Por supuesto, nosotros podemos combinar tanto la función select como la función where.

>>> from pipe import select, where

>>> resultado = listado | select(lambda x: x ** 2) | where(lambda x: x > 100) 
>>> list(resultado)
[121, 1156, 144, 225, 256]

Para este ejemplo generamos un listado con todos aquellos elementos elevados al cuadrado cuyo valor sea mayor que 100.

Bastante cool ¿No lo crees?

Agrupamiento

Lo interesante de la librería pipe no solo recae en el uso de la función select o where, no para nada, ya que atreves de esta librería seremos capaces, inclusive, de agrupar elementos. Sí, así como lo lees. Esto sin duda me recuerda mucho a LinQ de C#.

Veamos un par de ejemplos.

De mismo listado, generemos 2 grupos. Elementos mayores igual a 10 y elementos menores a 10. El código pudiera quedar de la siguiente manera.

>>> resultado = listado | groupby( lambda x: "Mayores" if x > 10 else "Menores" )
>>> resultado
<itertools.groupby at 0x7fc1e7316728>

Al utilizar la función groupby obtendremos como resultado un objeto de tipo iterador.

Este objeto no es más que una colección de tuplas. Tuplas con 2 elementos. El Id por el cual se realizo el agrupamiento y los elementos agrupados.

Si queremos iterar los elementos agrupado, podemos hacerlo mediante un ciclo for.

Nota: Recordemos que al tratarse de un objeto de tipo iterador, una vez iteremos sobre todos los elementos de dicha colección, no será posible reutilizar este iterador una segunda ocasión, por lo tanto solo queda generar nuevamente el agrupamiento. 🤯

>>> resultado = listado | groupby( lambda x: "Mayores" if x > 10 else "Menores" )

>>>  for grupo, elementos in resultado:
    ...:     print(grupo, ':', list(elementos))

Mayores : [11, 34, 12, 15, 16]
Menores : [4, 6, 8, 10, 9]

Para este ejemplo convertimos todos los elementos agrupados en un listado, el cual fácilmente podemos manipular, sin embargo recordemos que estos también podemos hacerlo utilizando la función select.

Hagamos un refactor al agrupamiento.

>>> agrupamiento = lambda x: "Mayores" if x > 10 else "Menores"
>>> resultado = listado | groupby( agrupamiento ) | select( lambda x: { x[0]: list(x[1]) }  )
>>>  resultado = list(resultado)

>>> resultado
[{'Mayores': [11, 34, 12, 15, 16]}, {'Menores': [4, 6, 8, 10, 9]}]

Este es sin duda un agrupamiento mucho mejor, ya que dejamos de lado el utilizar iteradores y procedemos a crear un listado de diccionarios. Diccionarios que hacen referencia a cada uno de los grupos creados, y que su vez almacenan los elementos agrupados.

En este caso fueron 2 grupos, sin embargo podemos generar la n cantidad de grupos que deseemos.

Por ejemplo, creemos 3 grupos. Elementos menores a 10, elementos iguales a 10 y elementos mayores a 10. 😱

def validador(elemento):
    if elemento > 10:
        return 'Mayores'
    elif elemento == 10:
        return 'Iguales'
    else:
         return 'Menores'

>>> resultado = listado | groupby(validador) | select( lambda x: { x[0]: list(x[1]) }  )
>>> resultado = list(resultado)

[{'Iguales': [10]},
 {'Mayores': [11, 34, 12, 15, 16]},
 {'Menores': [4, 6, 8, 9]}]

En este caso como el agrupamiento requiere de mayor lógica he definido la función validador, Y paso como argumento dicha función.

Elementos duplicados.

Ya para finalizar el post me gustaría mencionar que con la librería pipe seremos capaces de eliminar elementos duplicados de una colección. Esto gracias a la función dedup. Función que sin duda me ha sacado de un par de apuros. 🐜

>>> from pipe import where, dedup

>>> listado = [4, 6, 8, 10, 9, 11,  12, 12, 12, 11, 12]
>>> resultado = listado | where(lambda x: x > 10)  | dedup(lambda x: x % 2 == 0)
>>> resultado = list(resultado)

>>> resultado
[11, 12]

De igual forma la función recibe como argumento la función que debe aplicarse sobre cada uno de los elementos en la colección.

Si bien esto puede sonar algo trivial, ya que recordemos en Python existen los sets (Colecciones que no permiten elementos duplicados) también es importante recordar que estos no puede ser utilizados en una secuencia de ejecución, como si lo hace dedup. 🐍

Conclusión

Listo, ahora ya sabes como poder implementar pipes con Python. Sin bien es cierto en esta ocasión trabajamos con un par de funciones de la librería pipe, es importante mencionar que no son las única. Es por ello que, antes de finalizar la lectura, me gustaría compartirte el link del Repositorio Oficial donde es posible conocer más acerca de esta asombrosa herramienta.

Espero el post haya sido de tu agrado, y si fue así, no olvides hacérnoslo saber en la sección de comentarios. 🍻


¿El contenido te resulto de ayuda?

Para poder dejar tu opinión es necesario ser un usuario autenticado. Login

Más Tips y Ejercicios 🐍

Adquiere una subscripción PyWombat por tan solo $3 USD. al mes.

Conoce los beneficios de ser usuario premium:
Niveles desbloqueados: Ten accesos a todos los niveles de ejercicios. 🔓
Nuevo límite: Incrementa tu límite de ejercicios por semana. 🚀
Contenido único: Recibe semanalmente recursos exclusivos de Python (Videos, Artículos y Capitulos del libro PyWombat, comienza como desarrollador Python. 🐍