Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre otra tecnica para generar un valor a la vez.

Anuncios

Tecnicamente son iguales a las comprensiones de list pero en lugar de hacerlo con corchetes ([]) lo hacemos con parentesis () pero la unica diferencia es que solo permiten una sola iteracion y luego finalizan, analicemos el siguiente ejemplo:

>>> cubos = [v ** 3 for v in range(10)]
>>> cubos
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
>>> type(cubos)
<class 'list'>
>>>
Anuncios

Esto que generamos es una comprension de tipo list como vinimos trabajando hasta ahora, si lo comprobamos veremos que tenemos los resultados que necesitabamos y si chequeamos el tipo sera de list, pasemos a crear lo mismo con una expresion generadora:

>>> cubos_gen = (v ** 3 for v in range(10))
>>> cubos_gen
<generator object <genexpr> at 0x7f6274327750>
>>> type(cubos_gen)
<class 'generator'>
>>>
Anuncios

Aca podemos ver como su sintaxis es similar a la vista anteriormente pero la diferencia esta en los elementos que la contienen, si lo probamos al no ser de tipo list no nos devuelve ningun valor sino una referencia a donde esta almacenada y si vemos el tipo nos devuelve que es un generador pero como lo vemos? Aca entra en accion justamente list, para ello primero crearemos un alias:

>>> _ = list
Anuncios

Y luego lo visualizaremos de la siguiente forma:

>>> _(cubos_gen)
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
>>>
Anuncios

Como podemos ver nos genero la iteracion y nos devolvio los valores pero si lo volvemos a llamar para ver sus valores sucedera esto:

>>> _(cubos_gen)
[]
>>>
Anuncios

A esto nos referimos cuando dijimos al principio de que solo realiza una iteracion y luego la finaliza, si necesitamos recuperar estos valores debemos volver a crear los mismos como se hizo la primera vez, esto es el ejemplo mas basico veamos como podemos utilizarlos con map y filter con los siguientes ejemplos, veamos el primero:

>>> def agregador(*n) :
...     return sum(n)
... 
>>>
Anuncios

Primero definiremos una funcion para hacer la sumatoria del valor recibido, vamos a ponerlo en accion mediante la siguiente linea:

>>> s1 = sum(map(lambda *n: agregador(*n), range(100), range(1, 101)))
Anuncios

En este caso volveremos a hacer la sumatoria del map, para ello usaremos una funcion anonima que llamara a agregador y le enviara el valor generado por los dos range que usamos, el primer valor comenzara desde cero y el otro desde 1, por lo tanto cada map sera de (0,1) y luego (1,2) asi sucesivamente, el valor que obtendremos de la funcion sera la suma de estos dos valores, por lo que ejecutaremos la funcion sum como si fuese sum(1, 3, 5 ….), esto nos devolvera un valor de 10000, vamos a transformar esta comprension en una expresion generadora:

s2 = sum(agregador(*n) for n in zip(range(100), range(1,101)))
Anuncios

Esto hace exactamente lo mismo pero de una forma «monona» y se lee mejor que el caso anterior, pasemos a ver un ejemplo con filter:

>>> cubos = [x ** 3 for x in range(10)]
>>> 
>>> cubos_impares1 = filter(lambda cubo: cubo % 2, cubos)
>>> cubos_impares2 = (cubo for cubo in cubos if cubo % 2)
>>>
Anuncios

Primero crearemos una variable llamada cubos y calcularemos los valores al cubo de un rango de numeros, despues buscaremos solamente los cubos impares, para usaremos primero una operacion con filter donde usaremos la funcion anonima para obtener ese valor desde lo realizado en cubos, todo esto lo almacenaremos en cubos_impares1, luego haremos lo mismo pero con una expresion generada donde usaremos un bucle con un condicional donde verifica si el numero es impar, vamos a probar el primer caso:

>>> _(cubos_impares1)
[1, 27, 125, 343, 729]
>>>
Anuncios

Esto es lo obtenido en el primer caso, veamos el segundo:

>>> _(cubos_impares2)
[1, 27, 125, 343, 729]
>>> _(cubos_impares2)
[]
>>>
Anuncios

Obtenemos exactamente lo mismo pero si lo intentamos recuperar nuevamente sucedera lo que comentamos en el primer ejemplo de este post, para el siguiente ejemplo vamos a unir a map y filter y para ello escribiremos el siguiente codigo:

>>> N = 20
>>> cubos1 = map(lambda n: (n, n ** 3),
...     filter(lambda n: n % 3 == 0 or n % 5 == 0, range(N))
... )
>>>
Anuncios

Primero estableceremos el limite en N, lo siguiente sera utilizar un map que almacenara los valores del rango y su cubo que establecemos con el limite anterior, pero filtaremos para que se almacene solamente cuando sean multiplos de 3 o de 5, si lo probamos veremos lo siguiente:

>>> _(cubos1)
[(0, 0), (3, 27), (5, 125), (6, 216), (9, 729), (10, 1000), (12, 1728), (15, 3375), (18, 5832)]
>>>
Anuncios

Vamos a tomar esta operacion y vamos a convertirla en una expresion generadora:

>>> cubos2 = ((n, n ** 3) for n in range(N) 
...     if n % 3 == 0 or n % 5 == 0)
>>>
Anuncios

Esta es una version mejorada, donde primero generaremos los tuples que deseamos, despues el bucle y por ultimo el condicional para que pasen solamente los multiplos de 3 y 5, si lo chequeamos veremos lo siguiente:

>>> _(cubos2)
[(0, 0), (3, 27), (5, 125), (6, 216), (9, 729), (10, 1000), (12, 1728), (15, 3375), (18, 5832)]
>>> _(cubos2)
[]
>>>
Anuncios

Como vemos actua como siempre, nos devuelve el resultado una sola vez pero siempre de una forma mas simple y elegante que la version original, para entender realmente cual es la practicidad de trabajar con este tipo de expresion vamos a analizar el siguiente ejemplo:

>>> print(sum([n ** 2 for n in range(10 ** 8)]))
Anuncios

Esta es la sumatoria de un valor elevado al cuadrado y ese valor es determinado por un bucle que va desde 0 a 100000000 (10⁸), si lo ejecutamos veremos que se demora un tiempo y nos devuelve la siguiente salida:

>>> print(sum([n ** 2 for n in range(10 ** 8)]))
Terminado (killed)
tinchicus@dbn001vrt:~/python$
Anuncios

Como no puede procesar este numero ya sea por tiempo o memoria, el sistema determina que debe finalizarlo para poder liberarse, como ven no solamente elimina al proceso sino que tambien nos quita del interprete, vamos a hacer el mismo ejemplo pero por una expresion generadora:

>>> print(sum(n ** 2 for n in range(10 ** 8)))
333333328333333350000000
>>>
Anuncios

Ohhh funciono!, esto es debido a su forma de trabajar de esta expresion, recuerden que solo genera la iteracion una vez, por lo tanto como no debe retenerla no necesita reservar un espacio de memoria como cualquiera de los otros objetos que vimos anteriormente o en el ejemplo, es decir que esta es una herramienta mas util para el proceso de enormes valores que uno mas bajo pero mas adelante veremos como utilizarlos apropiadamente.

Anuncios

En resumen, hoy hemos visto el otro tipo de generador, hemos visto como trabajan, como nos permiten reemplazar a otras comprensiones, tambien hemos visto cual es el beneficio de poder trabajar con este tipo de generadores, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios
pp258

Donación

Es para mantenimento del sitio, gracias!

$1.00