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:

  1. Ao invocar uma função contendo yield, o interpretador não executa o corpo imediatamente: ele retorna um objeto gerador.
  2. Cada chamada a next(gerador) avança a execução até o próximo yield, entrega o valor e congela o frame de execução na PVM (frame->f_lasti).
  3. As variáveis locais permanecem intactas no Heap até o próximo disparo.
  4. 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