Map, filter e reduce na prática
1. Introdução às Funções de Alta Ordem
Python trata funções como objetos de primeira classe — você pode passá-las como argumentos, retorná-las de outras funções e armazená-las em variáveis. Esse recurso é a base do paradigma funcional, que nos permite escrever código mais declarativo e menos propenso a efeitos colaterados.
Considere a diferença entre um loop tradicional e uma abordagem funcional:
# Loop tradicional
numeros = [1, 2, 3, 4, 5]
quadrados = []
for n in numeros:
quadrados.append(n ** 2)
# Abordagem funcional
quadrados = list(map(lambda x: x ** 2, numeros))
A versão funcional é mais concisa, expressa a intenção diretamente ("mapeie cada elemento para seu quadrado") e elimina variáveis mutáveis intermediárias. As três funções protagonistas desse estilo são map(), filter() e reduce(), disponíveis nativamente (as duas primeiras como built-ins, a última no módulo functools).
2. map(): Transformando Iteráveis
A função map() aplica uma transformação a cada elemento de um ou mais iteráveis, retornando um iterador lazy. Sua sintaxe é map(função, iterável, ...).
Exemplo 1 — Conversão de tipos e formatação:
# Converter strings para inteiros
valores = ["10", "20", "30", "40"]
inteiros = list(map(int, valores)) # [10, 20, 30, 40]
# Formatar preços com duas casas decimais
precos = [49.9, 129.5, 9.99]
formatados = list(map("R$ {:.2f}".format, precos))
# ['R$ 49.90', 'R$ 129.50', 'R$ 9.99']
Exemplo 2 — Lambda para transformações rápidas:
nomes = ["ana", "joão", "maria"]
nomes_capitalizados = list(map(lambda s: s.capitalize(), nomes))
# ['Ana', 'João', 'Maria']
Exemplo 3 — Múltiplos iteráveis simultâneos:
lista1 = [1, 2, 3, 4]
lista2 = [10, 20, 30, 40]
produtos = list(map(lambda x, y: x * y, lista1, lista2))
# [10, 40, 90, 160]
Quando múltiplos iteráveis são fornecidos, map() para quando o menor deles se esgota.
3. filter(): Selecionando Dados com Critérios
filter() constrói um iterador contendo apenas os elementos para os quais a função booleana retorna True. Sintaxe: filter(função_booleana, iterável).
Exemplo 1 — Filtrar números pares:
numeros = range(1, 11)
pares = list(filter(lambda x: x % 2 == 0, numeros))
# [2, 4, 6, 8, 10]
Exemplo 2 — Remover valores nulos ou falsy:
dados = [0, "Python", "", None, 42, False, "map"]
limpos = list(filter(None, dados))
# ['Python', 42, 'map']
Passar None como função faz com que filter() remova todos os valores considerados falsy (0, None, False, "", [], etc).
Exemplo 3 — Filtrar com funções mais complexas:
def tem_letra_a(palavra):
return 'a' in palavra.lower()
palavras = ["casa", "carro", "moto", "avião", "navio"]
com_a = list(filter(tem_letra_a, palavras))
# ['casa', 'carro', 'avião', 'navio']
Comparação com list comprehension: filter() é mais explícito semanticamente — você está filtrando, não apenas iterando. List comprehensions com if são mais flexíveis, mas filter() comunica melhor a intenção de seleção.
4. reduce(): Acumulando Resultados
Diferente de map() e filter(), reduce() não é uma built-in — precisa ser importada de functools. Ela aplica uma função de dois argumentos cumulativamente aos elementos de um iterável, reduzindo-o a um único valor.
from functools import reduce
Sintaxe: reduce(função_acumuladora, iterável, valor_inicial_opcional)
Exemplo 1 — Soma acumulada:
numeros = [1, 2, 3, 4, 5]
soma = reduce(lambda acc, x: acc + x, numeros, 0)
# 15
Exemplo 2 — Produto de todos os elementos:
fatores = [2, 3, 4, 5]
produto = reduce(lambda acc, x: acc * x, fatores, 1)
# 120
Exemplo 3 — Encontrar o máximo:
valores = [45, 12, 89, 33, 71]
maximo = reduce(lambda a, b: a if a > b else b, valores)
# 89
Cuidados: reduce() pode tornar o código menos legível. Para operações comuns como soma, produto, mínimo ou máximo, prefira funções built-in (sum(), min(), max()) ou loops explícitos.
5. Combinações Poderosas: Encadeando Funções
O verdadeiro poder surge ao combinar map(), filter() e reduce() em pipelines de processamento.
Exemplo real — Processamento de dados de vendas:
from functools import reduce
vendas = [
{"produto": "notebook", "quantidade": 3, "preco": 3500.00},
{"produto": "mouse", "quantidade": 10, "preco": 50.00},
{"produto": "teclado", "quantidade": 5, "preco": 200.00},
{"produto": "monitor", "quantidade": 2, "preco": 1200.00},
{"produto": "mousepad", "quantidade": 0, "preco": 30.00},
]
# Pipeline: filtrar itens com quantidade > 0, calcular total por item, somar tudo
total_vendas = reduce(
lambda acc, x: acc + x,
map(
lambda v: v["quantidade"] * v["preco"],
filter(lambda v: v["quantidade"] > 0, vendas)
),
0.0
)
# total_vendas = 3*3500 + 10*50 + 5*200 + 2*1200 = 10500 + 500 + 1000 + 2400 = 14400.0
Comparação com compreensões aninhadas:
# Equivalente mais legível
total_vendas = sum(v["quantidade"] * v["preco"] for v in vendas if v["quantidade"] > 0)
Para pipelines simples, comprehensions ou generator expressions são mais pythonicos. Para transformações complexas com múltiplos estágios, o encadeamento funcional pode ser mais claro.
6. Alternativas Modernas e Boas Práticas
List comprehensions são o substituto idiomático para map() e filter() na maioria dos casos:
# map com lambda
quadrados = list(map(lambda x: x**2, range(10)))
# Equivalente idiomático
quadrados = [x**2 for x in range(10)]
# filter com lambda
pares = list(filter(lambda x: x % 2 == 0, range(10)))
# Equivalente idiomático
pares = [x for x in range(10) if x % 2 == 0]
Generator expressions oferecem lazy evaluation, assim como map() e filter():
# Lazy com map
quadrados_lazy = map(lambda x: x**2, range(10))
# Lazy com generator expression
quadrados_lazy = (x**2 for x in range(10))
Quando evitar reduce(): Para operações que possuem funções built-in, use-as:
# Evite
soma = reduce(lambda a, b: a + b, numeros, 0)
# Prefira
soma = sum(numeros)
# Evite
todos_pares = reduce(lambda a, b: a and b % 2 == 0, numeros, True)
# Prefira
todos_pares = all(x % 2 == 0 for x in numeros)
Regra prática: Use map() e filter() quando já tiver uma função nomeada para aplicar; use list comprehensions para transformações simples com lambdas; use reduce() apenas quando não existir alternativa built-in e a lógica de acumulação for clara.
7. Casos Práticos no Mundo Real
Processamento de logs — filtrar erros e extrair informações:
logs = [
"2024-01-15 10:30:45 INFO Conexão estabelecida",
"2024-01-15 10:31:02 ERROR Timeout ao acessar banco",
"2024-01-15 10:31:15 WARNING Pouca memória disponível",
"2024-01-15 10:32:00 ERROR Falha na autenticação",
]
# Extrair apenas mensagens de erro
erros = list(filter(lambda linha: "ERROR" in linha, logs))
# Extrair timestamps dos erros
timestamps_erro = list(map(lambda linha: linha.split()[0:2], erros))
# [['2024-01-15', '10:31:02'], ['2024-01-15', '10:32:00']]
Análise financeira — média móvel com reduce():
from functools import reduce
precos_acoes = [100, 102, 101, 105, 110, 108, 107, 112, 115, 113]
janela = 3
def media_moveis(dados, janela):
return reduce(
lambda acc, _: acc + [sum(dados[len(acc):len(acc)+janela]) / janela],
range(len(dados) - janela + 1),
[]
)
medias = media_moveis(precos_acoes, janela)
# [101.0, 102.67, 105.33, 107.67, 108.33, 109.0, 111.33, 113.33]
Limpeza de dados — normalizar strings e remover outliers:
dados_brutos = [" João ", "MARIA", "ana", None, "PEDRO", "", " "]
# Normalizar: remover espaços, capitalizar, ignorar nulos/vazios
limpos = list(filter(None, map(lambda s: s.strip().capitalize() if s else None, dados_brutos)))
# ['João', 'Maria', 'Ana', 'Pedro']
Combinando com itertools para processamento avançado:
from itertools import islice, chain
# Processar em lotes de 3
dados = range(1, 13)
lotes = [list(islice(dados, i, i+3)) for i in range(0, 12, 3)]
somas_lotes = list(map(sum, lotes))
# [6, 15, 24, 33]
Esses exemplos mostram como map(), filter() e reduce() resolvem problemas reais de processamento de dados de forma elegante e funcional. A escolha entre eles e alternativas modernas depende sempre do contexto, legibilidade e desempenho desejado.
Referências
- Documentação oficial: map() — Referência completa da função built-in map, com exemplos de uso e detalhes sobre múltiplos iteráveis.
- Documentação oficial: filter() — Especificação da função filter, incluindo o comportamento com None como argumento.
- Documentação oficial: functools.reduce() — Documentação do módulo functools com detalhes sobre reduce, valor inicial e casos de borda.
- Real Python: Python's map(), filter(), and reduce() Functions — Tutorial prático e abrangente sobre as três funções, com exemplos comparativos e boas práticas.
- GeeksforGeeks: map(), filter() and reduce() in Python — Artigo técnico com exemplos variados e explicações detalhadas sobre cada função.
- Python.org: Functional Programming HOWTO — Guia oficial sobre programação funcional em Python, abordando map, filter, reduce e itertools.
- Programiz: Python map(), filter(), reduce() with Examples — Tutoriais interativos com exemplos executáveis e explicações visuais.