Herramientas de iteración de Python: filter(), islice(), map() y zip()

En esta guía, veremos las herramientas de iteración integradas en Python 3: filter(), map() y zip(), así como la función islice() del módulo itertools con ejemplos.

Introducción

Python ha tocado los corazones de muchos desarrolladores de software en todo el mundo, gracias a su utilidad y simplicidad.

Python proporciona a sus usuarios una serie de funciones y estructuras de datos útiles que facilitan el trabajo con datos, incluidas las herramientas utilizadas para recorrer los datos de manera eficiente, conocidas como itertools.

Esta guía le mostrará cómo usar Python itertools para iterar a través de objetos a través de:

  • filter() - La función filter() toma una secuencia proporcionada o iterable junto con un criterio de filtrado (una función o lambda). Luego prueba cada elemento en la secuencia para determinar si el elemento se ajusta a los criterios de filtrado, devolviendo solo los elementos que coinciden con esos criterios.
  • islice() - La función islice() permite al usuario recorrer un iterable con start y stop, y devuelve un generador.
  • map() - La función map() crea un objeto de mapa iterable que aplica una transformación específica a cada elemento en un iterable elegido.
  • zip() - La función zip() toma dos objetos iterables y devuelve una tupla de elementos emparejados. El primer elemento de ambos iterables se empareja, el segundo elemento de ambos iterables se empareja, y así sucesivamente.

Comenzaremos definiendo objetos iterables y funciones de iteración y luego procederemos a ver algunos ejemplos de las cuatro funciones de iteración mencionadas anteriormente.

Nota: A partir de Python 3, filter(), map() y zip() son funcionalmente equivalentes a las funciones itertools ifilter(), imap( ) y izip(). Todos devuelven iteradores y no requieren importaciones.

islice() no se transfirió al espacio de nombres integrado de Python 3. Aún tendrá que importar el módulo itertools para usarlo.

¿Qué son los objetos iterables?

Un objeto iterable/iterable se puede definir como un contenedor que contiene datos que se pueden repetir/iterar. Los objetos iterables en Python incluyen listas, conjuntos, tuplas y diccionarios.

Por lo general, cuando trabajamos con objetos iterables, los recorremos utilizando herramientas básicas como bucles for. A menudo ignoramos las características y herramientas que puede tener un lenguaje que pueden ayudarnos con tareas iterativas. Las herramientas de iteración ofrecen funciones eficientes y estandarizadas (similares a las funciones que vería en lenguajes de programación funcionales como Haskell) que se integran con otras funciones iterativas para simplificar las tareas iterativas en solo unas pocas líneas de código.

La función filtro()

filter() es una función incorporada y nos permite tomar un grupo de elementos iterables y probar si los elementos dentro del iterable cumplen con los criterios de filtro especificados:

1
filter(function, iterable)

Dado que filter() devuelve un generador (objeto filter), lo envolveremos en una lista() para volver a convertirlo en una lista simple. Si tuviéramos que filtrar a través de declaraciones for y if, se vería algo así como:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Create a simple list numbered 0 to 10
number_list = [x for x in range(0,10)]

# Will filter for even numbers
even_numbers = []
for number in number_list:
    if number%2 == 0:
        even_numbers.append(number)

print(even_numbers)

Esto resulta en:

1
[0, 2, 4, 6, 8]

Por el contrario, podríamos haber logrado este mismo resultado usando filter() y pasando en la misma condición. Si se cumple la condición y se devuelve Verdadero, no se filtra. Si la condición no se cumple y se devuelve False, el elemento iterable se filtra.

Esta condición se puede proporcionar como una función anónima - lambda o como una función independiente:

1
2
3
4
5
number_list = [x for x in range(0,10)]

filtered_list = list(filter(lambda number: number % 2 == 0, number_list))

print(filtered_list)

Cuando se le proporciona una lambda, el número es un elemento del iterable que estamos filtrando actualmente. Para cada número, verificamos si es divisible por 2. Si es así, está incluido en la nueva salida:

1
[0, 2, 4, 6, 8]

Siempre que la función devuelva Verdadero o Falso, puede extraer la función para que sea independiente y simplemente hacer referencia a ella aquí en lugar de usar lambda:

1
2
3
4
5
6
7
number_list = [x for x in range(0,10)]

def is_even(number):
    return number%2==0
    
filtered_list = list(filter(is_even, number_list))
print(filtered_list)

Otra función similar a filter(), llamada filterfalse(), se puede encontrar en itertools. Esta es una contrapartida de filter() que devuelve los elementos que no satisfacen la condición. Después de importar la función desde itertools podemos usar nuestro código anterior y aplicar filterfalse() para obtener solo los números impares de la lista:

1
2
3
4
5
6
from itertools import filterfalse
number_list = [x for x in range(0,10)]

filtered_list = list(filterfalse(lambda number: number % 2 == 0, number_list))

print(filtered_list)

Esto da como resultado una lista filtrada de números impares:

1
[1, 3, 5, 7, 9]

En lugar de una función anónima, aquí también puede usar una función independiente:

1
2
3
4
5
6
7
8
9
from itertools import filterfalse
number_list = [x for x in range(0,10)]

def is_even(number):
    return number%2==0

filtered_list = list(filterfalse(is_even, number_list))

print(filtered_list)

La función islice()

La función islice() es parte de la biblioteca itertools, y toma un objeto iterable y devuelve un segmento de él, entre los elementos definidos por los argumentos start y end dados a la función:

1
itertools.islice(iterable, start, end)

Vamos a islice() una cadena. Dado que esto devuelve un generador, lo envolveremos en una lista para contener el resultado también. Si omite el argumento “inicio”, la función se dividirá hasta el argumento “final” proporcionado obligatoriamente. Si se proporcionan ambos, se dividirá entre ellos y devolverá ese segmento:

1
2
3
from itertools import islice
old_string = "I need this, but not this"
print(list(islice(old_string, 11)))

Aquí, hemos cortado old_string desde su inicio hasta el elemento 11:

1
['I', ' ', 'n', 'e', 'e', 'd', ' ', 't', 'h', 'i', 's']

Sin embargo, si proporcionamos un argumento start, podemos dividir un segmento específico:

1
2
3
4
from itertools import islice
old_string = "I need this, but not this"

print(list(islice(old_string, 7, 11)))
1
['t', 'h', 'i', 's']

Por lo general, cuando trabajamos con iterables, queremos terminar con un iterable, como una lista. Sin embargo, el rebanado también es una operación común para las cadenas, en cuyo caso, normalmente queremos una cadena, no una lista. Afortunadamente, es fácil unir() los elementos de la lista de nuevo en una cadena:

1
print(''.join(list(islice(old_string, 0, 11))))

Aquí, hemos unido cada elemento a una cadena vacía, lo que da como resultado que el segmento cortado se convierta en una cadena:

1
I need this

La función mapa()

La función map toma un objeto iterable y una función que aplica una transformación a todos los elementos del iterable:

1
map(function, iterable)

La función map() está incluida en las funciones integradas de Python, por lo que no es necesario importar nada. map() ofrece exactamente la misma funcionalidad que imap() del módulo itertools en Python 2.

En términos generales, es muy útil cuando desea realizar transformaciones por lotes en cada elemento de un iterable. Cada elemento se asigna a una versión transformada de ese elemento, o al resultado de otra operación realizada por o sobre ese elemento.

Digamos que te gustaría elevar cada elemento entero a la potencia de 2:

1
2
3
4
5
6
number_list = [x for x in range(0,10)]

numbers_powered = []
for number in number_list:
    numbers_powered.append(number**2)
print(numbers_powered)

Esto resulta en una secuencia de:

1
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Ahora, podemos simplificar esto con un map():

1
print(list(map(lambda x: x**2, number_list)))

Para cada elemento en el iterable number_list: el elemento se eleva a la potencia de dos y se coloca en una nueva lista:

1
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Por supuesto, en lugar de una función anónima, también puede definir otras funciones:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
number_list = [x for x in range(0,10)]

def function(number):
    print("Performing transformation on number ", number)
    return number**2

print('Original list: ', number_list)

mapped_list = list(map(function, number_list))

print('Transformed list: ', mapped_list)

Esto resulta en:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Original list:  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Performing transformation on number  0
Performing transformation on number  1
Performing transformation on number  2
Performing transformation on number  3
Performing transformation on number  4
Performing transformation on number  5
Performing transformation on number  6
Performing transformation on number  7
Performing transformation on number  8
Performing transformation on number  9
Transformed list:  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

La función zip()

La función zip() acepta objetos iterables 0..n y crea tuplas 0..n que contienen el elemento nth de cada uno de estos iterables:

1
zip(iterable_1, iterable_2, iterable_3...)

Es una función integrada desde Python 3 y ofrece la misma funcionalidad izip() del módulo itertools ofrecido en Python 2.

Vamos a zip() juntos una lista de nombres y una lista de ID, donde el primer nombre se comprime con la primera ID, el segundo nombre se comprime con la segunda ID, etcétera:

1
2
3
4
names_list = ['Francis', 'Drake', 'Alexander', 'Robert', 'Elon']
id_list = ['001', '002', '003', '004', '005']

print(list(zip(names_list,id_list)))

Esto resulta en:

1
[('Francis', '001'), ('Drake', '002'), ('Alexander', '003'), ('Robert', '004'), ('Elon', '005')]

Nota: Si estos iterables no tienen la misma forma, como names_list con 5 elementos y id_list con 10 elementos, solo se asignarán los primeros 5, mientras que el resto de id_list se asignará ignorado Se asignará la secuencia común más larga.

Como de costumbre, esto devuelve un generador, por lo que lo hemos envuelto en una lista ().

La misma funcionalidad y comportamiento está presente con más de dos iterables; de hecho, puede proporcionar un número ilimitado de ellos:

1
2
3
4
5
6
names_list = ['Francis', 'Drake', 'Alexander', 'Robert', 'Elon']
last_name_list = ['Brown', 'Johnson', 'Tiedemann', 'Mann']
id_list = ['001', '002', '003', '004', '005']

zipped_list = list(zip(names_list, last_name_list, id_list))
print(zipped_list)
1
[('Francis', 'Brown', '001'), ('Drake', 'Johnson', '002'), ('Alexander', 'Tiedemann', '003'), ('Robert', 'Mann', '004')]

Dado que names_list tiene una longitud de 5, mientras que los otros dos iterables tienen una longitud de 4, el último elemento de names_list no tiene un par.

Esta es una gran herramienta para agrupar elementos relacionados que aparecen en diferentes contextos.

Conclusión

Python viene con una serie de funciones integradas que ayudan a los ingenieros a manipular datos de manera fácil y eficiente, a través de una API de alto nivel. La iteración es una operación muy común, y las herramientas de iteración de Python son muy útiles para operaciones de estilo funcional de una sola línea en elementos.

En esta guía, hemos echado un vistazo a las funciones filter(), map(), islice() y zip().

Aunque islice() reside en el módulo itertools, y no está presente en el espacio de nombres incorporado, es un tipo de función que usará comúnmente para subsecuenciar otras secuencias, y se usa comúnmente con las otras funciones destacadas en la guía.i

Licensed under CC BY-NC-SA 4.0