<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> é "lazy" (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):
"""Converte temperatura de Celsius para Fahrenheit."""
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 = ["Alice", "Bob", "Carlos"]
idades = [25, 30, 28]
Função que recebe dois argumentos
def apresentar(nome, idade):
return f"{nome} tem {idade} anos"
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 = [
{"produto": "Notebook", "valor": 3500, "vendido": True},
{"produto": "Mouse", "valor": 50, "vendido": False},
{"produto": "Teclado", "valor": 200, "vendido": True},
{"produto": "Monitor", "valor": 1200, "vendido": True},
]
Passo 1: Filtrar apenas vendas realizadas
Passo 2: Extrair apenas o valor de cada venda
vendas_realizadas = list(
map(
lambda v: v["valor"],
filter(lambda v: v["vendido"], vendas)
)
)
print(vendas_realizadas) # [3500, 200, 1200]
print(f"Total: R$ {sum(vendas_realizadas)}") # 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):
"""Verifica se um usuário está ativo."""
return usuario.get("ativo", False)
usuários = [
{"nome": "Ana", "ativo": True},
{"nome": "Bruno", "ativo": False},
{"nome": "Carlos", "ativo": True},
]
usuários_ativos = list(filter(é_ativo, usuários))
print(usuários_ativos)
[{'nome': 'Ana', 'ativo': True}, {'nome': 'Carlos', 'ativo': 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 > 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):
"""Combina dois dicionários, mantendo valores do primeiro em caso de conflito."""
resultado = {dict1, dict2}
return resultado
dicts = [
{"a": 1, "b": 2},
{"b": 3, "c": 4},
{"c": 5, "d": 6},
]
resultado_final = reduce(combinar_dicts, dicts)
print(resultado_final) # {'a': 1, 'b': 3, 'c': 5, 'd': 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):
"""Formata um valor segundo um padrão."""
return formato.format(valor)
formatar_moeda = partial(formatar_valor, formato="R$ {:.2f}")
formatar_porcentagem = partial(formatar_valor, formato="{:.1%}")
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):
"""Calcula o n-ésimo número de Fibonacci com cache."""
if n < 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"Resultado: {resultado1}")
print(f"Primeira chamada: {tempo1:.4f}s")
print(f"Segunda chamada: {tempo2:.6f}s (muito mais rápida!)")</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 "infinitos" 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("A", 3))) # ['A', 'A', 'A']
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 = ["Chocolate", "Baunilha", "Morango"]
duplas = combinations(sabores, 2)
print(list(duplas))
[('Chocolate', 'Baunilha'), ('Chocolate', 'Morango'), ('Baunilha', 'Morango')]
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 = ["Chrome", "Firefox"]
sistemas_operacionais = ["Windows", "Linux"]
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():
"""Simula leitura infinita de logs."""
contador = 0
while True:
contador += 1
yield f"Log {contador}: evento processado"
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 = "par" if chave == 0 else "ímpar"
print(f"{tipo}: {list(grupo)}")
par: [2, 4, 6]
ímpar: [1, 3, 5, 7, 9]
Agrupando registros por categoria
vendas = [
{"produto": "Notebook", "categoria": "Eletrônicos", "vendas": 10},
{"produto": "Mouse", "categoria": "Eletrônicos", "vendas": 25},
{"produto": "Livro", "categoria": "Livros", "vendas": 15},
{"produto": "Caneta", "categoria": "Papelaria", "vendas": 50},
]
Primeiro, ordenar pela categoria
vendas_ordenadas = sorted(vendas, key=itemgetter("categoria"))
Depois, agrupar
for categoria, grupo in groupby(vendas_ordenadas, key=itemgetter("categoria")):
produtos = [item["produto"] for item in grupo]
print(f"{categoria}: {produtos}")
Eletrônicos: ['Notebook', 'Mouse']
Livros: ['Livro']
Papelaria: ['Caneta']</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 = [
{"id": 1, "cliente": "Ana", "itens": [{"produto": "Notebook", "preço": 3000, "qtd": 1}]},
{"id": 2, "cliente": "Bruno", "itens": [{"produto": "Mouse", "preço": 50, "qtd": 2}]},
{"id": 3, "cliente": "Carlos", "itens": [{"produto": "Teclado", "preço": 200, "qtd": 1}, {"produto": "Monitor", "preço": 1200, "qtd": 1}]},
{"id": 4, "cliente": "Ana", "itens": []}, # 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):
"""Calcula o valor total de um pedido."""
valor_total = sum(
item["preço"] * item["qtd"]
for item in pedido["itens"]
)
return {**pedido, "valor_total": valor_total}
Executar o pipeline
pedidos_processados = list(
map(
calcular_valor_pedido,
filter(lambda p: len(p["itens"]) > 0, pedidos)
)
)
Ordenar por valor descendente
pedidos_ordenados = sorted(pedidos_processados, key=lambda p: p["valor_total"], reverse=True)
Exibir resultado
for pedido in pedidos_ordenados:
print(f"Pedido {pedido['id']} - {pedido['cliente']}: R$ {pedido['valor_total']:.2f}")
Calcular valor total de todos os pedidos com reduce
valor_total_geral = reduce(
lambda total, p: total + p["valor_total"],
pedidos_ordenados,
0
)
print(f"\nValor total de pedidos processados: R$ {valor_total_geral:.2f}")</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><!-- FIM --></p>