Python

O que Todo Dev Deve Saber sobre Programação Funcional em Python: map, filter, functools e itertools

18 min de leitura

O que Todo Dev Deve Saber sobre Programação Funcional em Python: map, filter, functools e itertools

Fundamentos da Programação Funcional em Python A programação funcional é um paradigma que trata a computação como a avaliação de funções matemáticas, evitando mudança de estado e dados mutáveis. Python, embora seja multiparadigma, oferece suporte robusto para este estilo através de construções nativas. A grande vantagem dessa abordagem é escrever código mais previsível, testável e fácil de raciocinar, já que funções puras (aquelas que não modificam estado externo e retornam sempre o mesmo resultado para as mesmas entradas) são o coração dessa filosofia. No contexto prático, programação funcional em Python significa trabalhar com operações sobre coleções de dados de forma declarativa — você descreve o quê quer fazer, não como fazer. Isso contrasta com a programação imperativa, onde você detalha cada passo da execução. As ferramentas que veremos ( , , e ) são exatamente os instrumentos que facilitam essa transformação mental e prática do seu código. A Função : Transformando Dados O Conceito por Trás do A função aplica

<h2>Fundamentos da Programação Funcional em Python</h2>

<p>A programação funcional é um paradigma que trata a computação como a avaliação de funções matemáticas, evitando mudança de estado e dados mutáveis. Python, embora seja multiparadigma, oferece suporte robusto para este estilo através de construções nativas. A grande vantagem dessa abordagem é escrever código mais previsível, testável e fácil de raciocinar, já que funções puras (aquelas que não modificam estado externo e retornam sempre o mesmo resultado para as mesmas entradas) são o coração dessa filosofia.</p>

<p>No contexto prático, programação funcional em Python significa trabalhar com operações sobre coleções de dados de forma declarativa — você descreve <em>o quê</em> quer fazer, não <em>como</em> fazer. Isso contrasta com a programação imperativa, onde você detalha cada passo da execução. As ferramentas que veremos (<code>map</code>, <code>filter</code>, <code>functools</code> e <code>itertools</code>) são exatamente os instrumentos que facilitam essa transformação mental e prática do seu código.</p>

<h2>A Função <code>map()</code>: Transformando Dados</h2>

<h3>O Conceito por Trás do <code>map()</code></h3>

<p>A função <code>map()</code> aplica uma função a cada elemento de uma sequência e retorna um iterador com os resultados. Ela é fundamental quando você precisa transformar todos os elementos de uma coleção de forma uniforme. Em vez de escrever um loop <code>for</code>, você expressa a intenção de forma mais limpa e direta.</p>

<pre><code class="language-python"># Abordagem imperativa (tradicional)

números = [1, 2, 3, 4, 5]

quadrados = []

for num in números:

quadrados.append(num ** 2)

print(quadrados) # [1, 4, 9, 16, 25]

Abordagem funcional com map()

números = [1, 2, 3, 4, 5]

quadrados = list(map(lambda x: x ** 2, números))

print(quadrados) # [1, 4, 9, 16, 25]</code></pre>

<p>Note que <code>map()</code> retorna um iterador, não uma lista. Isso é uma otimização de memória — em Python 3, <code>map()</code> é &quot;lazy&quot; (preguiçoso), só computando valores conforme necessário. Se você precisar de uma lista imediata, use <code>list()</code>.</p>

<h3>Usando Funções Nomeadas com <code>map()</code></h3>

<p>Embora lambdas sejam convenientes, funções nomeadas tornam o código mais legível quando a lógica é complexa. Veja como <code>map()</code> trabalha perfeitamente com qualquer função:</p>

<pre><code class="language-python">def converter_celsius_para_fahrenheit(celsius):

&quot;&quot;&quot;Converte temperatura de Celsius para Fahrenheit.&quot;&quot;&quot;

return (celsius * 9/5) + 32

temperaturas_celsius = [0, 10, 20, 30, 40]

temperaturas_fahrenheit = list(map(converter_celsius_para_fahrenheit, temperaturas_celsius))

print(temperaturas_fahrenheit) # [32.0, 50.0, 68.0, 86.0, 104.0]</code></pre>

<h3><code>map()</code> com Múltiplas Sequências</h3>

<p>Uma capacidade poderosa do <code>map()</code> é processar múltiplas sequências em paralelo, passando elementos correspondentes para a função:</p>

<pre><code class="language-python"># Combinando duas listas

nomes = [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Carlos&quot;]

idades = [25, 30, 28]

Função que recebe dois argumentos

def apresentar(nome, idade):

return f&quot;{nome} tem {idade} anos&quot;

apresentacoes = list(map(apresentar, nomes, idades))

for apresentacao in apresentacoes:

print(apresentacao)

Alice tem 25 anos

Bob tem 30 anos

Carlos tem 28 anos</code></pre>

<h2>A Função <code>filter()</code>: Selecionando Dados Relevantes</h2>

<h3>O Conceito e Aplicação Prática</h3>

<p><code>filter()</code> reduz uma sequência mantendo apenas os elementos que atendem a um critério. A função passada como argumento deve retornar um valor booleano. Como <code>map()</code>, <code>filter()</code> também retorna um iterador lazy, economizando memória em grandes datasets.</p>

<pre><code class="language-python"># Abordagem imperativa

números = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

pares = []

for num in números:

if num % 2 == 0:

pares.append(num)

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

Abordagem funcional com filter()

números = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

pares = list(filter(lambda x: x % 2 == 0, números))

print(pares) # [2, 4, 6, 8, 10]</code></pre>

<h3>Combinando <code>map()</code> e <code>filter()</code></h3>

<p>A verdadeira potência da programação funcional emerge quando você combina operações. Um padrão comum é filtrar dados e depois transformá-los:</p>

<pre><code class="language-python"># Dados brutos de vendas

vendas = [

{&quot;produto&quot;: &quot;Notebook&quot;, &quot;valor&quot;: 3500, &quot;vendido&quot;: True},

{&quot;produto&quot;: &quot;Mouse&quot;, &quot;valor&quot;: 50, &quot;vendido&quot;: False},

{&quot;produto&quot;: &quot;Teclado&quot;, &quot;valor&quot;: 200, &quot;vendido&quot;: True},

{&quot;produto&quot;: &quot;Monitor&quot;, &quot;valor&quot;: 1200, &quot;vendido&quot;: True},

]

Passo 1: Filtrar apenas vendas realizadas

Passo 2: Extrair apenas o valor de cada venda

vendas_realizadas = list(

map(

lambda v: v[&quot;valor&quot;],

filter(lambda v: v[&quot;vendido&quot;], vendas)

)

)

print(vendas_realizadas) # [3500, 200, 1200]

print(f&quot;Total: R$ {sum(vendas_realizadas)}&quot;) # Total: R$ 4900</code></pre>

<h3>Filtrando com Funções Nomeadas</h3>

<p>Para lógica mais complexa, usar funções nomeadas melhora a legibilidade:</p>

<pre><code class="language-python">def é_ativo(usuario):

&quot;&quot;&quot;Verifica se um usuário está ativo.&quot;&quot;&quot;

return usuario.get(&quot;ativo&quot;, False)

usuários = [

{&quot;nome&quot;: &quot;Ana&quot;, &quot;ativo&quot;: True},

{&quot;nome&quot;: &quot;Bruno&quot;, &quot;ativo&quot;: False},

{&quot;nome&quot;: &quot;Carlos&quot;, &quot;ativo&quot;: True},

]

usuários_ativos = list(filter(é_ativo, usuários))

print(usuários_ativos)

[{&#039;nome&#039;: &#039;Ana&#039;, &#039;ativo&#039;: True}, {&#039;nome&#039;: &#039;Carlos&#039;, &#039;ativo&#039;: True}]</code></pre>

<h2>O Módulo <code>functools</code>: Operações Avançadas sobre Funções</h2>

<h3><code>reduce()</code>: Agregando Dados</h3>

<p><code>functools.reduce()</code> combina todos os elementos de uma sequência usando uma função binária (que recebe dois argumentos), retornando um único valor. É essencial para operações cumulativas como soma, produto ou concatenação:</p>

<pre><code class="language-python">from functools import reduce

Somando números

números = [1, 2, 3, 4, 5]

soma = reduce(lambda x, y: x + y, números)

print(soma) # 15

Encontrando o maior valor

números = [3, 7, 2, 9, 1]

máximo = reduce(lambda x, y: x if x &gt; y else y, números)

print(máximo) # 9

Multiplicando todos os elementos (fatorial)

números = [1, 2, 3, 4, 5]

fatorial = reduce(lambda x, y: x * y, números)

print(fatorial) # 120</code></pre>

<h3>Usando <code>reduce()</code> com Funções Nomeadas</h3>

<p>Para operações complexas, uma função nomeada deixa a intenção clara:</p>

<pre><code class="language-python">from functools import reduce

def combinar_dicts(dict1, dict2):

&quot;&quot;&quot;Combina dois dicionários, mantendo valores do primeiro em caso de conflito.&quot;&quot;&quot;

resultado = {dict1, dict2}

return resultado

dicts = [

{&quot;a&quot;: 1, &quot;b&quot;: 2},

{&quot;b&quot;: 3, &quot;c&quot;: 4},

{&quot;c&quot;: 5, &quot;d&quot;: 6},

]

resultado_final = reduce(combinar_dicts, dicts)

print(resultado_final) # {&#039;a&#039;: 1, &#039;b&#039;: 3, &#039;c&#039;: 5, &#039;d&#039;: 6}</code></pre>

<h3><code>partial()</code>: Criando Variações de Funções</h3>

<p><code>functools.partial()</code> cria uma nova função fixando alguns argumentos de uma função existente. É útil para adaptar funções genéricas a contextos específicos:</p>

<pre><code class="language-python">from functools import partial

def potência(base, expoente):

return base ** expoente

Criando uma função especializada

ao_quadrado = partial(potência, expoente=2)

ao_cubo = partial(potência, expoente=3)

print(ao_quadrado(5)) # 25

print(ao_cubo(5)) # 125

Exemplo prático: adaptando uma função de processamento de strings

def formatar_valor(formato, valor):

&quot;&quot;&quot;Formata um valor segundo um padrão.&quot;&quot;&quot;

return formato.format(valor)

formatar_moeda = partial(formatar_valor, formato=&quot;R$ {:.2f}&quot;)

formatar_porcentagem = partial(formatar_valor, formato=&quot;{:.1%}&quot;)

print(formatar_moeda(1234.5678)) # R$ 1234.57

print(formatar_porcentagem(0.856)) # 85.6%</code></pre>

<h3><code>cache()</code>: Otimizando Chamadas Repetidas</h3>

<p>O decorador <code>@functools.cache</code> armazena resultados de chamadas anteriores, evitando recálculos desnecessários. É perfeito para funções puras e custosas:</p>

<pre><code class="language-python">from functools import cache

import time

@cache

def fibonacci(n):

&quot;&quot;&quot;Calcula o n-ésimo número de Fibonacci com cache.&quot;&quot;&quot;

if n &lt; 2:

return n

return fibonacci(n-1) + fibonacci(n-2)

Primeira chamada (recalcula)

início = time.time()

resultado1 = fibonacci(35)

tempo1 = time.time() - início

Segunda chamada (usa cache)

início = time.time()

resultado2 = fibonacci(35)

tempo2 = time.time() - início

print(f&quot;Resultado: {resultado1}&quot;)

print(f&quot;Primeira chamada: {tempo1:.4f}s&quot;)

print(f&quot;Segunda chamada: {tempo2:.6f}s (muito mais rápida!)&quot;)</code></pre>

<h2>O Módulo <code>itertools</code>: Combinações e Permutações</h2>

<h3><code>chain()</code>: Encadeando Sequências</h3>

<p><code>itertools.chain()</code> concatena múltiplas sequências em um único iterador, sem criar cópias intermediárias:</p>

<pre><code class="language-python">from itertools import chain

lista1 = [1, 2, 3]

lista2 = [4, 5, 6]

lista3 = [7, 8, 9]

Encadeando três listas

todas = chain(lista1, lista2, lista3)

print(list(todas)) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

Muito mais eficiente que concatenação com +

Especialmente importante em loops com muitas sequências

for valor in chain([1, 2], [3, 4], [5, 6]):

print(valor)</code></pre>

<h3><code>repeat()</code> e <code>cycle()</code>: Iteradores Infinitos</h3>

<p>Esses geradores criam iteradores &quot;infinitos&quot; que são úteis em combinação com funções como <code>zip()</code> e <code>map()</code>:</p>

<pre><code class="language-python">from itertools import repeat, cycle, islice

repeat() gera o mesmo valor N vezes (ou infinitamente)

print(list(repeat(&quot;A&quot;, 3))) # [&#039;A&#039;, &#039;A&#039;, &#039;A&#039;]

cycle() percorre uma sequência continuamente

contador = cycle([1, 2, 3])

print(list(islice(contador, 7))) # [1, 2, 3, 1, 2, 3, 1]

Caso prático: aplicar a mesma operação com valores diferentes

preços_unitários = [10, 20, 15]

quantidades = [5, 3, 4]

total_por_produto = list(map(lambda p, q: p * q, preços_unitários, quantidades))

print(total_por_produto) # [50, 60, 60]</code></pre>

<h3><code>combinations()</code> e <code>permutations()</code>: Gerando Variações</h3>

<p>Essas funções geram todas as combinações ou permutações possíveis de uma sequência:</p>

<pre><code class="language-python">from itertools import combinations, permutations

Combinações: subconjuntos sem considerar ordem

sabores = [&quot;Chocolate&quot;, &quot;Baunilha&quot;, &quot;Morango&quot;]

duplas = combinations(sabores, 2)

print(list(duplas))

[(&#039;Chocolate&#039;, &#039;Baunilha&#039;), (&#039;Chocolate&#039;, &#039;Morango&#039;), (&#039;Baunilha&#039;, &#039;Morango&#039;)]

Permutações: ordenações diferentes

dois_algarismos = permutations([1, 2, 3], 2)

print(list(dois_algarismos))

[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

Caso prático: gerar todas as combinações possíveis de testes

navegadores = [&quot;Chrome&quot;, &quot;Firefox&quot;]

sistemas_operacionais = [&quot;Windows&quot;, &quot;Linux&quot;]

combinações_testes = list(combinations(navegadores + sistemas_operacionais, 2))

Você agora tem todas as duplas para teste de compatibilidade</code></pre>

<h3><code>islice()</code>: Extração Elegante de Subsequências</h3>

<p><code>itertools.islice()</code> extrai uma fatia de um iterador sem convertê-lo em lista, mantendo a eficiência de memória:</p>

<pre><code class="language-python">from itertools import islice, count

Pegando os primeiros 5 números naturais

primeiros_cinco = islice(count(1), 5)

print(list(primeiros_cinco)) # [1, 2, 3, 4, 5]

Pulando elementos: começar no índice 2, pegar 4 elementos

números = range(10)

seleção = islice(números, 2, 6)

print(list(seleção)) # [2, 3, 4, 5]

Processando apenas os primeiros 1000 registros de um grande arquivo

def ler_logs_do_servidor():

&quot;&quot;&quot;Simula leitura infinita de logs.&quot;&quot;&quot;

contador = 0

while True:

contador += 1

yield f&quot;Log {contador}: evento processado&quot;

Pegar apenas os 10 primeiros logs

primeiros_logs = list(islice(ler_logs_do_servidor(), 10))

for log in primeiros_logs:

print(log)</code></pre>

<h3><code>groupby()</code>: Agrupando Dados por Chave</h3>

<p><code>itertools.groupby()</code> agrupa elementos consecutivos que compartilham a mesma chave:</p>

<pre><code class="language-python">from itertools import groupby

from operator import itemgetter

Agrupando números por paridade

números = [1, 3, 5, 2, 4, 6, 7, 9]

agrupados = groupby(sorted(números, key=lambda x: x % 2), key=lambda x: x % 2)

for chave, grupo in agrupados:

tipo = &quot;par&quot; if chave == 0 else &quot;ímpar&quot;

print(f&quot;{tipo}: {list(grupo)}&quot;)

par: [2, 4, 6]

ímpar: [1, 3, 5, 7, 9]

Agrupando registros por categoria

vendas = [

{&quot;produto&quot;: &quot;Notebook&quot;, &quot;categoria&quot;: &quot;Eletrônicos&quot;, &quot;vendas&quot;: 10},

{&quot;produto&quot;: &quot;Mouse&quot;, &quot;categoria&quot;: &quot;Eletrônicos&quot;, &quot;vendas&quot;: 25},

{&quot;produto&quot;: &quot;Livro&quot;, &quot;categoria&quot;: &quot;Livros&quot;, &quot;vendas&quot;: 15},

{&quot;produto&quot;: &quot;Caneta&quot;, &quot;categoria&quot;: &quot;Papelaria&quot;, &quot;vendas&quot;: 50},

]

Primeiro, ordenar pela categoria

vendas_ordenadas = sorted(vendas, key=itemgetter(&quot;categoria&quot;))

Depois, agrupar

for categoria, grupo in groupby(vendas_ordenadas, key=itemgetter(&quot;categoria&quot;)):

produtos = [item[&quot;produto&quot;] for item in grupo]

print(f&quot;{categoria}: {produtos}&quot;)

Eletrônicos: [&#039;Notebook&#039;, &#039;Mouse&#039;]

Livros: [&#039;Livro&#039;]

Papelaria: [&#039;Caneta&#039;]</code></pre>

<h2>Um Exemplo Integrado: Pipeline Funcional Completo</h2>

<p>Para solidificar o aprendizado, vamos construir um pipeline real que combine todas essas ferramentas:</p>

<pre><code class="language-python">from functools import reduce

from itertools import filterfalse

import operator

Dataset: pedidos de um e-commerce

pedidos = [

{&quot;id&quot;: 1, &quot;cliente&quot;: &quot;Ana&quot;, &quot;itens&quot;: [{&quot;produto&quot;: &quot;Notebook&quot;, &quot;preço&quot;: 3000, &quot;qtd&quot;: 1}]},

{&quot;id&quot;: 2, &quot;cliente&quot;: &quot;Bruno&quot;, &quot;itens&quot;: [{&quot;produto&quot;: &quot;Mouse&quot;, &quot;preço&quot;: 50, &quot;qtd&quot;: 2}]},

{&quot;id&quot;: 3, &quot;cliente&quot;: &quot;Carlos&quot;, &quot;itens&quot;: [{&quot;produto&quot;: &quot;Teclado&quot;, &quot;preço&quot;: 200, &quot;qtd&quot;: 1}, {&quot;produto&quot;: &quot;Monitor&quot;, &quot;preço&quot;: 1200, &quot;qtd&quot;: 1}]},

{&quot;id&quot;: 4, &quot;cliente&quot;: &quot;Ana&quot;, &quot;itens&quot;: []}, # Pedido vazio

]

Pipeline:

1. Filtrar pedidos que têm itens

2. Para cada pedido, calcular o valor total

3. Ordenar por valor descendente

4. Exibir resultado

def calcular_valor_pedido(pedido):

&quot;&quot;&quot;Calcula o valor total de um pedido.&quot;&quot;&quot;

valor_total = sum(

item[&quot;preço&quot;] * item[&quot;qtd&quot;]

for item in pedido[&quot;itens&quot;]

)

return {**pedido, &quot;valor_total&quot;: valor_total}

Executar o pipeline

pedidos_processados = list(

map(

calcular_valor_pedido,

filter(lambda p: len(p[&quot;itens&quot;]) &gt; 0, pedidos)

)

)

Ordenar por valor descendente

pedidos_ordenados = sorted(pedidos_processados, key=lambda p: p[&quot;valor_total&quot;], reverse=True)

Exibir resultado

for pedido in pedidos_ordenados:

print(f&quot;Pedido {pedido[&#039;id&#039;]} - {pedido[&#039;cliente&#039;]}: R$ {pedido[&#039;valor_total&#039;]:.2f}&quot;)

Calcular valor total de todos os pedidos com reduce

valor_total_geral = reduce(

lambda total, p: total + p[&quot;valor_total&quot;],

pedidos_ordenados,

0

)

print(f&quot;\nValor total de pedidos processados: R$ {valor_total_geral:.2f}&quot;)</code></pre>

<p>Este exemplo mostra como a programação funcional torna o código mais expressivo e fácil de entender, comparado a múltiplos loops aninhados.</p>

<h2>Conclusão</h2>

<p>Ao longo desta aula, exploramos as ferramentas fundamentais da programação funcional em Python. <strong>Primeiro</strong>, <code>map()</code> e <code>filter()</code> permitem transformar e selecionar dados de forma declarativa, tornando o código mais legível e menos propenso a erros de lógica impeditiva. <strong>Segundo</strong>, o módulo <code>functools</code> nos fornece operações poderosas como <code>reduce()</code> para agregações, <code>partial()</code> para especializar funções, e <code>cache()</code> para otimização, ampliando significativamente o que podemos expressar de forma funcional. <strong>Terceiro</strong>, <code>itertools</code> oferece geradores eficientes em memória para combinações, permutações, encadeamento e agrupamento, permitindo trabalhar com grandes volumes de dados sem overhead.</p>

<p>A verdadeira maestria em programação funcional vem de saber <em>quando</em> aplicar cada ferramenta. Nem todo código deve ser funcional — o equilibrio entre legibilidade, performance e contexto é essencial. Use essas técnicas quando elas clarificarem a intenção do código, não como um fim em si mesmo.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://docs.python.org/3/library/functools.html" target="_blank" rel="noopener noreferrer">Python Official Documentation - functools</a></li>

<li><a href="https://docs.python.org/3/library/itertools.html" target="_blank" rel="noopener noreferrer">Python Official Documentation - itertools</a></li>

<li><a href="https://realpython.com/python-map-function/" target="_blank" rel="noopener noreferrer">Real Python - Map, Filter, and Reduce</a></li>

<li><a href="https://realpython.com/python-functional-programming/" target="_blank" rel="noopener noreferrer">Real Python - Functional Programming in Python</a></li>

<li><a href="https://www.oreilly.com/library/view/fluent-python-2nd/9781492126249/" target="_blank" rel="noopener noreferrer">Fluent Python - Luciano Ramalho (Capítulo sobre Funções)</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Python

Como Usar asyncio em Python: Event Loop, Coroutines e Tasks em Produção
Como Usar asyncio em Python: Event Loop, Coroutines e Tasks em Produção

Introdução ao Asyncio: O Coração da Programação Assíncrona em Python Asyncio...

Guia Completo de FastAPI Avançado: Depends, Middleware, Background Tasks e Websockets
Guia Completo de FastAPI Avançado: Depends, Middleware, Background Tasks e Websockets

Dependency Injection com Depends A injeção de dependências é um padrão fundam...

Clean Architecture em Python: Estruturando Projetos para Escalar na Prática
Clean Architecture em Python: Estruturando Projetos para Escalar na Prática

Clean Architecture em Python: Estruturando Projetos para Escalar A Clean Arch...