W3docs

Python enumerate, zip, map y filter

Aprende cómo funcionan enumerate, zip, map y filter en Python, con ejemplos claros y patrones prácticos para una iteración más limpia.

Python incluye cuatro funciones integradas — enumerate, zip, map y filter — que reemplazan patrones de bucle comunes con una expresión única y legible. Cada una devuelve un iterador (perezoso y eficiente en memoria), y se combinan de forma natural con los bucles for y las comprensiones de lista.

Este capítulo cubre:

  • enumerate() — recorre con un índice automático
  • zip() — itera sobre dos o más secuencias en paralelo
  • map() — aplica una función a cada elemento
  • filter() — conserva solo los elementos que pasan una prueba
  • Combinación de las cuatro en patrones del mundo real
  • Cuándo preferir una comprensión de lista en su lugar

enumerate() — índice durante la iteración

Cuando necesitas tanto la posición como el valor durante un bucle, el enfoque ingenuo utiliza un contador manual:

colors = ["red", "green", "blue"]
i = 0
for color in colors:
    print(i, color)
    i += 1

enumerate() hace esto automáticamente. Envuelve cualquier iterable y produce pares (índice, valor):

colors = ["red", "green", "blue"]
for i, color in enumerate(colors):
    print(i, color)

Salida:

0 red
1 green
2 blue

Comenzar desde un índice diferente

Pasa un argumento start para empezar a contar desde cualquier entero:

fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")

Salida:

1. apple
2. banana
3. cherry

Esto es útil al construir listas numeradas para mostrar, donde la numeración desde 0 resulta extraña para el lector.

enumerate() con una condición

Como enumerate() es simplemente un iterador normal, puedes agregar pruebas if dentro del cuerpo del bucle:

scores = [72, 91, 55, 88, 44]
for rank, score in enumerate(scores, start=1):
    if score >= 80:
        print(f"Rank {rank}: score {score} — PASS")

Salida:

Rank 2: score 91 — PASS
Rank 4: score 88 — PASS

Obtener solo el índice sin enumerate()

Si solo necesitas un índice y nada más, range(len(seq)) funciona — pero tan pronto como también necesites el valor, usa enumerate() para evitar escribir seq[i] repetidamente.


zip() — iterar sobre varias secuencias en paralelo

zip() combina dos o más iterables en un único iterador de tuplas, emparejando elementos en posiciones coincidentes:

names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]

for name, score in zip(names, scores):
    print(f"{name}: {score}")

Salida:

Alice: 95
Bob: 87
Charlie: 92

zip() con tres o más iterables

first = ["a", "b", "c"]
second = [1, 2, 3]
third = [True, False, True]

for x, y, z in zip(first, second, third):
    print(x, y, z)

Salida:

a 1 True
b 2 False
c 3 True

Secuencias de longitud desigual

zip() se detiene en el iterable más corto y descarta silenciosamente los elementos restantes de los más largos:

short = [1, 2]
long  = [10, 20, 30, 40]

print(list(zip(short, long)))

Salida:

[(1, 10), (2, 20)]

Los valores 30 y 40 de long se descartan. Si necesitas conservar todos los elementos, usa itertools.zip_longest de la biblioteca estándar:

from itertools import zip_longest

short = [1, 2]
long  = [10, 20, 30, 40]

print(list(zip_longest(short, long, fillvalue=0)))

Salida:

[(1, 10), (2, 20), (0, 30), (0, 40)]

Convertir un resultado zip en una lista o diccionario

zip() devuelve un iterador perezoso. Envuélvelo en list() para materializar todos los pares de una vez, o pásalo a dict() para construir un mapa a partir de dos secuencias paralelas:

keys   = ["name", "age", "city"]
values = ["Alice", 30, "Paris"]

record = dict(zip(keys, values))
print(record)

Salida:

{'name': 'Alice', 'age': 30, 'city': 'Paris'}

Descomprimir con zip(*...)

El truco de desempaquetado con * "invierte" una secuencia de tuplas de vuelta a secuencias separadas:

pairs = [(1, "a"), (2, "b"), (3, "c")]
numbers, letters = zip(*pairs)

print(numbers)  # (1, 2, 3)
print(letters)  # ('a', 'b', 'c')

map() — aplicar una función a cada elemento

map(function, iterable) aplica function a cada elemento y devuelve un iterador perezoso de resultados. Reemplaza un bucle for que construye una nueva lista transformando cada elemento.

numbers = [1, 2, 3, 4, 5]
squares = map(lambda x: x ** 2, numbers)
print(list(squares))

Salida:

[1, 4, 9, 16, 25]

map() con una función nombrada

Puedes pasar cualquier callable — una función integrada, una función definida con def, o una lambda:

words = ["  hello  ", "  world  ", "  python  "]
stripped = list(map(str.strip, words))
print(stripped)

Salida:

['hello', 'world', 'python']

Aquí str.strip se pasa como referencia (sin paréntesis). map() llama a str.strip(word) para cada elemento.

map() sobre múltiples iterables

Pasa iterables adicionales para mapear una función que toma dos o más argumentos. map() entonces recorre todos los iterables en paralelo (como zip(), se detiene en el más corto):

a = [1, 2, 3]
b = [10, 20, 30]
totals = list(map(lambda x, y: x + y, a, b))
print(totals)

Salida:

[11, 22, 33]

map() vs comprensión de lista

Ambos enfoques son válidos. La regla general:

SituaciónPreferir
Aplicar una función nombrada existentemap() — más corto, sin necesidad de lambda
Escribir una expresión en líneaComprensión de lista — más legible
Múltiples entradas en paralelomap() con múltiples iterables

Formas equivalentes:

# map() with a named function
result = list(map(str.upper, ["a", "b", "c"]))

# list comprehension — same result
result = [s.upper() for s in ["a", "b", "c"]]

filter() — conservar solo los elementos que cumplen una condición

filter(function, iterable) conserva los elementos para los cuales function devuelve un valor verdadero. Devuelve un iterador perezoso.

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = filter(lambda x: x % 2 == 0, numbers)
print(list(evens))

Salida:

[2, 4, 6, 8, 10]

filter() con una función nombrada

def is_positive(n):
    return n > 0

values = [-3, -1, 0, 4, 7, -2]
positives = list(filter(is_positive, values))
print(positives)

Salida:

[4, 7]

filter(None, iterable) — eliminar valores falsos

Pasar None como función conserva solo los elementos verdaderos. Es una forma rápida de eliminar cadenas vacías, ceros y valores None:

mixed = [0, "hello", "", None, 42, False, "world"]
truthy = list(filter(None, mixed))
print(truthy)

Salida:

['hello', 42, 'world']

filter() vs comprensión de lista

filter() y una comprensión de lista condicional hacen el mismo trabajo:

numbers = range(1, 11)

# filter()
evens_f = list(filter(lambda x: x % 2 == 0, numbers))

# list comprehension — equivalent
evens_c = [x for x in numbers if x % 2 == 0]

print(evens_f)  # [2, 4, 6, 8, 10]
print(evens_c)  # [2, 4, 6, 8, 10]

Prefiere una comprensión de lista cuando la condición está escrita en línea; prefiere filter() cuando ya tienes una función predicado bien nombrada.


Combinando enumerate, zip, map y filter

Estas cuatro funciones se combinan de forma natural. Aquí hay un ejemplo realista: tienes dos listas paralelas de nombres de estudiantes y puntajes brutos; quieres imprimir un informe numerado que incluya solo los estudiantes que aprobaron (puntaje >= 60), con los puntajes convertidos a calificaciones con letras.

def letter_grade(score):
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    return "F"

names  = ["Alice", "Bob", "Carol", "Dave", "Eve"]
scores = [85, 52, 91, 67, 48]

# Step 1: pair names with scores
pairs = zip(names, scores)

# Step 2: keep only passing students
passing = filter(lambda pair: pair[1] >= 60, pairs)

# Step 3: convert scores to letter grades
graded = map(lambda pair: (pair[0], letter_grade(pair[1])), passing)

# Step 4: number the results
for rank, (name, grade) in enumerate(graded, start=1):
    print(f"{rank}. {name}: {grade}")

Salida:

1. Alice: B
2. Carol: A
3. Dave: D

Cada paso produce un iterador; nada se materializa en una lista hasta que el bucle for lo consume. Esto mantiene el uso de memoria constante independientemente del tamaño de la entrada.


Errores comunes

Los iteradores se agotan después de un recorrido

map(), filter() y zip() devuelven iteradores, no listas. Una vez que consumes un iterador, queda vacío:

squares = map(lambda x: x ** 2, [1, 2, 3])
print(list(squares))  # [1, 4, 9]
print(list(squares))  # [] — already exhausted

Envuelve en list() desde el principio si necesitas iterar más de una vez.

zip() descarta silenciosamente los elementos sobrantes

Cuando las secuencias de entrada tienen longitudes diferentes, zip() descarta los elementos extra sin advertencia. Verifica siempre las longitudes explícitamente si esto importa, o usa itertools.zip_longest.

enumerate() no modifica el iterable

enumerate() envuelve el iterable original; no lo copia. Modificar la lista subyacente durante la iteración puede producir resultados inesperados — la misma precaución que aplica a los bucles for ordinarios.


Referencia rápida

FunciónFirmaQué devuelve
enumerateenumerate(iterable, start=0)Iterador de tuplas (índice, valor)
zipzip(*iterables)Iterador de tuplas, un elemento de cada iterable por tupla
mapmap(function, *iterables)Iterador de function aplicada a cada elemento
filterfilter(function, iterable)Iterador de elementos donde function devuelve True

Las cuatro son perezosas: producen valores bajo demanda en lugar de construir una lista completa de antemano.


Práctica

Práctica
What does enumerate(items, start=1) do when used in a for loop?
What does enumerate(items, start=1) do when used in a for loop?
Práctica
What happens when you pass sequences of different lengths to zip()?
What happens when you pass sequences of different lengths to zip()?
Práctica
Which statement about map() is correct?
Which statement about map() is correct?
Práctica
What does filter(None, iterable) do?
What does filter(None, iterable) do?
Was this page helpful?