Python enumerate, zip, map e filter
Aprenda como enumerate, zip, map e filter do Python funcionam, com exemplos claros e padrões práticos para um código de iteração mais limpo.
Python oferece quatro funções embutidas — enumerate, zip, map e filter — que substituem padrões comuns de loop com uma única expressão legível. Cada uma retorna um iterador (lazy, com uso eficiente de memória) e se compõem naturalmente com loops for e compreensões de lista.
Este capítulo aborda:
enumerate()— loop com índice automáticozip()— iterar sobre duas ou mais sequências em paralelomap()— aplicar uma função a cada itemfilter()— manter apenas os itens que passam em um teste- Combinando os quatro em padrões do mundo real
- Quando preferir uma compreensão de lista
enumerate() — índice durante o loop
Quando você precisa tanto da posição quanto do valor durante um loop, a abordagem ingênua usa um contador manual:
colors = ["red", "green", "blue"]
i = 0
for color in colors:
print(i, color)
i += 1enumerate() faz isso automaticamente. Ele envolve qualquer iterável e produz pares (índice, valor):
colors = ["red", "green", "blue"]
for i, color in enumerate(colors):
print(i, color)Saída:
0 red
1 green
2 blueComeçando a partir de um índice diferente
Passe um argumento start para começar a contar a partir de qualquer inteiro:
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")Saída:
1. apple
2. banana
3. cherryIsso é útil ao construir listas numeradas para exibição, onde a numeração baseada em 0 parece estranha ao leitor.
enumerate() com uma condição
Como enumerate() é apenas um iterador regular, você pode adicionar testes if dentro do corpo do loop:
scores = [72, 91, 55, 88, 44]
for rank, score in enumerate(scores, start=1):
if score >= 80:
print(f"Rank {rank}: score {score} — PASS")Saída:
Rank 2: score 91 — PASS
Rank 4: score 88 — PASSObtendo apenas o índice sem enumerate()
Se você só precisa de um índice e nada mais, range(len(seq)) funciona — mas assim que você também precisar do valor, use enumerate() para evitar escrever seq[i] repetidamente.
zip() — iterar sobre várias sequências em paralelo
zip() combina dois ou mais iteráveis em um único iterador de tuplas, emparelhando itens em posições correspondentes:
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
for name, score in zip(names, scores):
print(f"{name}: {score}")Saída:
Alice: 95
Bob: 87
Charlie: 92zip() com três ou mais iteráveis
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)Saída:
a 1 True
b 2 False
c 3 TrueSequências de comprimentos diferentes
zip() para na sequência mais curta e descarta silenciosamente os itens restantes das sequências mais longas:
short = [1, 2]
long = [10, 20, 30, 40]
print(list(zip(short, long)))Saída:
[(1, 10), (2, 20)]Os valores 30 e 40 de long são descartados. Se você precisar manter todos os itens, use itertools.zip_longest da biblioteca padrão:
from itertools import zip_longest
short = [1, 2]
long = [10, 20, 30, 40]
print(list(zip_longest(short, long, fillvalue=0)))Saída:
[(1, 10), (2, 20), (0, 30), (0, 40)]Convertendo um resultado zipado em lista ou dict
zip() retorna um iterador lazy. Envolva-o em list() para materializar todos os pares de uma vez, ou passe-o para dict() para construir um mapeamento a partir de duas sequências paralelas:
keys = ["name", "age", "city"]
values = ["Alice", 30, "Paris"]
record = dict(zip(keys, values))
print(record)Saída:
{'name': 'Alice', 'age': 30, 'city': 'Paris'}Descompactando com zip(*...)
O truque de desempacotamento com * "reverte" uma sequência de tuplas de volta em sequências separadas:
pairs = [(1, "a"), (2, "b"), (3, "c")]
numbers, letters = zip(*pairs)
print(numbers) # (1, 2, 3)
print(letters) # ('a', 'b', 'c')map() — aplicar uma função a cada item
map(function, iterable) aplica function a cada item e retorna um iterador lazy de resultados. Ele substitui um loop for que constrói uma nova lista transformando cada elemento.
numbers = [1, 2, 3, 4, 5]
squares = map(lambda x: x ** 2, numbers)
print(list(squares))Saída:
[1, 4, 9, 16, 25]map() com uma função nomeada
Você pode passar qualquer callable — uma função embutida, uma função definida com def ou uma lambda:
words = [" hello ", " world ", " python "]
stripped = list(map(str.strip, words))
print(stripped)Saída:
['hello', 'world', 'python']Aqui str.strip é passado como referência (sem parênteses). map() chama str.strip(word) para cada item.
map() sobre múltiplos iteráveis
Passe iteráveis adicionais para mapear uma função que recebe dois ou mais argumentos. map() então percorre todos os iteráveis em paralelo (como zip(), ele para no mais curto):
a = [1, 2, 3]
b = [10, 20, 30]
totals = list(map(lambda x, y: x + y, a, b))
print(totals)Saída:
[11, 22, 33]map() vs compreensão de lista
Ambas as abordagens são válidas. A regra geral:
| Situação | Prefira |
|---|---|
| Aplicar uma função nomeada existente | map() — mais curto, sem necessidade de lambda |
| Escrever uma expressão inline | Compreensão de lista — mais legível |
| Múltiplas entradas em paralelo | map() com múltiplos iteráveis |
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() — manter apenas os itens que correspondem a uma condição
filter(function, iterable) mantém os itens para os quais function retorna um valor verdadeiro. Retorna um iterador lazy.
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = filter(lambda x: x % 2 == 0, numbers)
print(list(evens))Saída:
[2, 4, 6, 8, 10]filter() com uma função nomeada
def is_positive(n):
return n > 0
values = [-3, -1, 0, 4, 7, -2]
positives = list(filter(is_positive, values))
print(positives)Saída:
[4, 7]filter(None, iterable) — remover valores falsy
Passar None como função mantém apenas os itens verdadeiros. Esta é uma maneira rápida de descartar strings vazias, zeros e valores None:
mixed = [0, "hello", "", None, 42, False, "world"]
truthy = list(filter(None, mixed))
print(truthy)Saída:
['hello', 42, 'world']filter() vs compreensão de lista
filter() e uma compreensão de lista condicional fazem o mesmo trabalho:
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]Prefira uma compreensão de lista quando a condição é escrita inline; prefira filter() quando você já tem uma função predicado bem nomeada.
Combinando enumerate, zip, map e filter
Essas quatro funções se compõem naturalmente. Aqui está um exemplo realista: você tem duas listas paralelas de nomes de alunos e notas brutas; quer imprimir um relatório numerado que inclui apenas os alunos aprovados (nota >= 60), com as notas convertidas em conceitos por 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}")Saída:
1. Alice: B
2. Carol: A
3. Dave: DCada etapa produz um iterador; nada é materializado em uma lista até que o loop for o consuma. Isso mantém o uso de memória constante independentemente do tamanho da entrada.
Armadilhas comuns
Iteradores são esgotados após uma única passagem
map(), filter() e zip() retornam iteradores, não listas. Uma vez que você consome um iterador, ele fica vazio:
squares = map(lambda x: x ** 2, [1, 2, 3])
print(list(squares)) # [1, 4, 9]
print(list(squares)) # [] — already exhaustedEnvolva em list() antecipadamente se precisar iterar mais de uma vez.
zip() descarta itens finais silenciosamente
Quando as sequências de entrada têm comprimentos diferentes, zip() descarta os elementos extras sem aviso. Sempre verifique os comprimentos explicitamente se isso for importante, ou use itertools.zip_longest.
enumerate() não altera o iterável
enumerate() envolve o iterável original; não o copia. Modificar a lista subjacente durante a iteração pode produzir resultados inesperados — o mesmo cuidado que se aplica a loops for comuns.
Referência rápida
| Função | Assinatura | O que retorna |
|---|---|---|
enumerate | enumerate(iterable, start=0) | Iterador de tuplas (index, value) |
zip | zip(*iterables) | Iterador de tuplas, um item de cada iterável por tupla |
map | map(function, *iterables) | Iterador de function aplicada a cada item |
filter | filter(function, iterable) | Iterador de itens onde function retorna True |
Todas as quatro são lazy: produzem valores sob demanda em vez de construir uma lista completa antecipadamente.