Capítulo 14: Paradigma Funcional e Avaliação Lazy: Lambdas, Functools, Comprehensions e Yield
Especialização em Sistemas com Python & POO com Type Hints • Capítulo 14
🗺️ Mapa Conceitual do Tópico
flowchart TD
subgraph FUNCTIONAL["Pilares do Paradigma Funcional em Python"]
FIRST_CLASS["Funções de Primeira Classe & Closures\nFunções como Argumentos e Retornos"]
PURE["Funções Puras & Imutabilidade\nPrevisibilidade Matemática & Zero Efeitos Colaterais"]
LAMBDA["Expressões Lambda Anônimas\nOrdenação Customizada (key=lambda x: ...)"]
end
subgraph FUNCTOOLS["O Arsenal do Módulo functools"]
LRU["@functools.lru_cache / @cache\nMemoização Automática & Alta Performance"]
PARTIAL["functools.partial\nAplicação Parcial de Argumentos & Currying Prático"]
REDUCE["functools.reduce\nAcumulação e Redução Funcional em Streams"]
end
subgraph LAZY_GEN["Avaliação Preguiçosa (Lazy Evaluation)"]
YIELD["Instrução yield & Suspensão de Frame na PVM\nProcessamento com Consumo Constante O(1) de RAM"]
GEN_EXP["Expressões Geradoras: (x for x in stream)\nAlternativa de Baixa Pegada a List Comprehensions"]
YIELD_FROM["yield from: Delegação Transparente de Subgeradores"]
end
FUNCTIONAL --> FUNCTOOLS
FUNCTIONAL --> LAZY_GEN
LAZY_GEN --> PIPELINE["Pipelines de Processamento de Big Data e Logs em Tempo Real"]
style FUNCTIONAL fill:#ede7f6,stroke:#512da8,stroke-width:2px
style FUNCTOOLS fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style LAZY_GEN fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
λ 1. Funções de Primeira Classe e o Módulo functools
Em Python, as funções são cidadãs de primeira classe (First-Class Citizens). Elas podem ser atribuídas a variáveis, passadas como argumentos para outras funções (Higher-Order Functions) e retornadas como resultado de expressões.
O Poder do Módulo functools
| Utilitário | Descrição e Comportamento | Impacto na Engenharia de Software |
|---|---|---|
@lru_cache |
Memoização automática dos resultados em cache (Least Recently Used). | Otimiza algoritmos recursivos e consultas repetidas de $O(2^N)$ para $O(N)$. |
@cache |
Variação simplificada do lru_cache sem teto máximo (maxsize=None). |
Máxima performance para funções puras matemáticas. |
partial() |
Fixa um subconjunto de argumentos em uma função pré-existente. | Reutilização limpa de funções genéricas sem duplicação de parâmetros. |
reduce() |
Aplica cumulativamente uma função binária aos elementos de um iterável. | Redução agregada de listas (soma, produto, concatenação de pipelines). |
@wraps |
Preserva nome (__name__) e docstrings (__doc__) em decoradores customizados. |
Evita que decorators ofusquem metadados em logs e documentações de API. |
from functools import lru_cache, partial
# Memoização instantânea em memória:
@lru_cache(maxsize=256)
def calcular_fator_risco(score: int, idade: int) -> float:
# Simulação de processamento analítico pesado
return (score * 1.5) / (idade + 1)
# Aplicação parcial: fixando taxa fixa em calculadora financeira
def calcular_juros(capital: float, taxa: float, tempo: int) -> float:
return capital * (1 + taxa) ** tempo
calcular_juros_credito_pessoal = partial(calcular_juros, taxa=0.035)
⚡ 2. Comprehensions vs map e filter
Embora map() e filter() sejam nativos, a comunidade Python consagrou as Comprehensions como o padrão idiomático para transformações e filtros:
- List Comprehension:
[expressao for item in col if condicao] - Set Comprehension:
{expressao for item in col if condicao} - Dict Comprehension:
{chave: valor for item in col if condicao}
# Abordagem Funcional Clássica (menos pythônica e exige casts):
nomes_maiusculos = list(map(str.upper, filter(lambda n: len(n) > 5, clientes)))
# Abordagem Idiomática Pythônica (otimizada em bytecode C e mais legível):
nomes_maiusculos = [n.upper() for n in clientes if len(n) > 5]
🌊 3. Avaliação Preguiçosa (Lazy Evaluation) com Geradores (yield)
Uma Expressão Geradora (expr for x in colecao) ou uma Função Geradora que utiliza a instrução yield não armazena a coleção de dados na memória RAM:
- Ao invocar uma função contendo
yield, o interpretador não executa o corpo imediatamente: ele retorna um objeto gerador. - Cada chamada a
next(gerador)avança a execução até o próximoyield, entrega o valor e congela o frame de execução na PVM (frame->f_lasti). - As variáveis locais permanecem intactas no Heap até o próximo disparo.
- Consumo de Memória: Constante $O(1)$, mesmo ao iterar sobre bilhões de registros ou arquivos de log de centenas de gigabytes.
from collections.abc import Generator
# Generator tipado sob PEP 484: Generator[YieldType, SendType, ReturnType]
def ler_arquivo_gigante_linhas(caminho: str) -> Generator[str, None, None]:
with open(caminho, mode="r", encoding="utf-8") as f:
for linha in f:
yield linha.strip()
# Delegação com yield from:
def agregar_multiplos_arquivos(*caminhos: str) -> Generator[str, None, None]:
for caminho in caminhos:
# Repassa o stream diretamente sem laço interno manual:
yield from ler_arquivo_gigante_linhas(caminho)
🔬 4. Arquitetura de Pipelines de Dados em Streaming
A composição de múltiplos geradores desacoplados forma um pipeline linear de dados no qual os elementos trafegam um a um através dos estágios de filtro e enriquecimento, sem materialização intermediária em disco ou RAM:
flowchart LR
FILE["Arquivo de Log (Disco 50GB)"] -->|"yield linha"| G1["Gerador 1: Leitor de Linhas (O(1) RAM)"]
G1 -->|"yield evento"| G2["Gerador 2: Parser JSON & Filtro 4xx/5xx"]
G2 -->|"yield métrica"| G3["Gerador 3: Enriquecimento Geográfico de IP"]
G3 -->|"sum() / reduce()"| CONSUMER["Consumidor / Dashboard de Alerta"]
style FILE fill:#ffebee,stroke:#f44336,stroke-width:2px
style G1 fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style G2 fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style G3 fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style CONSUMER fill:#fff3e0,stroke:#ff9800,stroke-width:2px