Programação funcional
Nesta aula, exploramos os fundamentos da programação funcional em Python, incluindo funções de alta ordem como map, filter e reduce, o módulo functools, imutabilidade e funções puras. A aula oferece exemplos práticos, boas práticas e exercícios para consolidar o aprendizado.
A programação funcional é um paradigma que trata a computação como a avaliação de funções matemáticas e evita mudanças de estado e dados mutáveis. Em Python, embora não seja uma linguagem puramente funcional, oferecemos ferramentas e padrões que nos permitem adotar esse estilo, tornando o código mais previsível, testável e conciso. Nesta aula, vamos explorar os conceitos essenciais e as ferramentas que Python disponibiliza para programação funcional.
Dominar esses conceitos é fundamental para escrever código mais limpo e expressivo, especialmente quando lidamos com transformações de dados em coleções. Além disso, muitos desses conceitos são amplamente utilizados em bibliotecas e frameworks modernos, como Pandas e Spark, então vale a pena compreendê-los a fundo.
map, filter, reduce
As funções map, filter e reduce são funções de alta ordem, ou seja, recebem outras funções como argumento. Elas são fundamentais para manipular coleções de forma declarativa, sem a necessidade de laços explícitos.
map aplica uma função a cada elemento de um iterável e retorna um novo iterável com os resultados. filter seleciona elementos que satisfazem uma condição (função que retorna booleano). reduce acumula os elementos de um iterável em um único valor, aplicando uma função binária cumulativamente. Em Python 3, reduce foi movida para o módulo functools, enquanto map e filter são builtins.
# Exemplo de map: elevar ao quadrado cada número
numeros = [1, 2, 3, 4]
quadrados = list(map(lambda x: x ** 2, numeros))
print(quadrados) # [1, 4, 9, 16]
# Exemplo de filter: selecionar apenas pares
pares = list(filter(lambda x: x % 2 == 0, numeros))
print(pares) # [2, 4]
# Exemplo de reduce: somar todos os números
from functools import reduce
soma = reduce(lambda acc, x: acc + x, numeros, 0)
print(soma) # 10
Observe que map e filter retornam objetos iteráveis (em Python 3, são generators), por isso usamos list() para materializar. Isso é eficiente para grandes conjuntos de dados, pois a computação é preguiçosa.
functools
O módulo functools fornece ferramentas para funções de alta ordem e operações com funções. Além de reduce, ele inclui partial (fixar argumentos), lru_cache (cache de resultados), wraps (para decoradores) e cmp_to_key (converte função de comparação em key).
Essas ferramentas aumentam a expressividade e a eficiência do código funcional. Por exemplo, partial permite criar novas funções a partir de outras, pré-preenchendo alguns argumentos, o que promove reuso e clareza.
from functools import partial, lru_cache
# partial: fixar o expoente em 2
potencia2 = partial(pow, exp=2)
print(potencia2(5)) # 25
# lru_cache: memoização para funções puras
@lru_cache(maxsize=None)
def fatorial(n):
if n < 2:
return 1
return n * fatorial(n-1)
print(fatorial(10)) # 3628800
print(fatorial.cache_info()) # CacheInfo(hits=0, misses=11, maxsize=None, currsize=11)
O uso de lru_cache é uma técnica clássica de programação funcional para evitar recálculos, desde que a função seja pura (mesmo resultado para os mesmos argumentos).
Imutabilidade
Imutabilidade significa que, uma vez criado, um objeto não pode ser modificado. Em Python, tipos como tuple, str, int e frozenset são imutáveis. A imutabilidade é um pilar da programação funcional, pois evita efeitos colaterais e torna o código mais previsível.
Trabalhar com dados imutáveis facilita a concorrência e o raciocínio sobre o código, já que não há risco de alterações acidentais. Em vez de modificar listas, criamos novas listas a partir das originais, usando compreensões ou funções como map e filter.
# Exemplo de imutabilidade: usar tuplas e criar novas estruturas
original = (1, 2, 3)
novo = original + (4,)
print(original) # (1, 2, 3)
print(novo) # (1, 2, 3, 4)
# Em vez de modificar uma lista, criamos uma nova
lista = [1, 2, 3]
lista_mais_um = [x + 1 for x in lista]
print(lista) # [1, 2, 3]
print(lista_mais_um) # [2, 3, 4]
Note que a imutabilidade não é obrigatória em Python, mas é uma boa prática quando se adota o estilo funcional. Podemos usar bibliotecas como types.MappingProxyType para criar dicionários somente-leitura.
Funções puras
Uma função pura é aquela que, dado o mesmo conjunto de entradas, sempre retorna o mesmo resultado e não possui efeitos colaterais (não modifica variáveis globais, não imprime, não altera argumentos, etc.). Funções puras são fáceis de testar e depurar, e são a base da programação funcional.
Em Python, podemos escrever funções puras seguindo boas práticas: evitar usar variáveis globais, não modificar argumentos mutáveis, e não realizar operações de I/O dentro delas. Isso contrasta com funções impuras, que podem depender de estado externo ou produzir efeitos colaterais.
# Função pura: não modifica nada externo
def somar(a, b):
return a + b
# Função impura: modifica uma variável global
contador = 0
def incrementar():
global contador
contador += 1
return contador
# Função impura: imprime na tela
def imprimir_soma(a, b):
print(a + b)
Preferir funções puras facilita a composição e o reuso. Por exemplo, podemos usar map com funções puras sem medo de efeitos inesperados.
Referências
- Documentação oficial de map()
- Documentação oficial de filter()
- Documentação do módulo functools
- Guia de programação funcional em Python (oficial)
- Real Python: Functional Programming in Python
- GeeksforGeeks: Python Programming Language
Exercícios
- Use
mappara converter uma lista de strings em uma lista de seus comprimentos. Exemplo:['casa', 'python', 'funcao']deve resultar em[4, 6, 7].✓ Resposta:palavras = ['casa', 'python', 'funcao'] comprimentos = list(map(len, palavras)) print(comprimentos) # [4, 6, 7] - Utilizando
filter, obtenha apenas os números positivos de uma lista que contém negativos, positivos e zero. Exemplo:[-1, 2, 0, -3, 5]deve resultar em[2, 5].✓ Resposta:numeros = [-1, 2, 0, -3, 5] positivos = list(filter(lambda x: x > 0, numeros)) print(positivos) # [2, 5] - Usando
reduce(defunctools), calcule o produto de todos os elementos de uma lista. Exemplo:[1, 2, 3, 4]deve resultar em24.✓ Resposta:from functools import reduce numeros = [1, 2, 3, 4] produto = reduce(lambda acc, x: acc * x, numeros, 1) print(produto) # 24 - Crie uma função pura chamada
dobrarque recebe um número e retorna o dobro. Não deve haver efeitos colaterais.✓ Resposta:def dobrar(x): return x * 2 - Explique por que a função
somarabaixo é pura, massomar_imprimirnão é:def somar(a, b): return a + bedef somar_imprimir(a, b): print(a + b).✓ Resposta: A funçãosomaré pura porque sempre retorna o mesmo resultado para os mesmos argumentos e não causa efeitos colaterais (não altera nada externo). Jásomar_imprimiré impura porque imprime na tela, o que é um efeito colateral observável, além de retornarNone.
Boas Práticas e Observações Finais
Ao adotar programação funcional em Python, lembre-se de que o estilo é uma ferramenta, não uma obrigação. Use map, filter e reduce quando tornarem o código mais legível; caso contrário, compreensões de lista podem ser mais claras. Prefira funções puras sempre que possível, mas não evite I/O em todo o programa — a ideia é isolá-lo em pontos específicos.
Além disso, explore o módulo itertools para operações avançadas com iteráveis, e considere bibliotecas como toolz ou fn.py para um suporte mais amplo ao paradigma funcional. A prática constante é essencial para internalizar esses conceitos.