💻 Exemplos: Capítulo 14: Paradigma Funcional e Avaliação Lazy: Lambdas, Functools, Comprehensions e Yield
Especialização em Sistemas com Python 3.12+ • Capítulo 14
🎯 Demonstração Prática: Pipeline Funcional de Processamento de Logs em Tempo Real
O exemplo abaixo implementa um pipeline analítico em streaming construído sob o paradigma funcional:
- Encadeamento de geradores (
yield) operando com consumo estritamente constante de memória ($O(1)$ RAM). - Utilização de
@functools.lru_cachepara memoização de resolução de geolocalização por IP. - Aplicação de
functools.reduceefunctools.partialpara redução estatística agregada de tráfego. - Filtros declarativos e transformações lazy com Type Hints estritos.
"""
demonstracao_funcional_e_geradores.py
Pipeline de Streaming Lazy com Geradores, functools (lru_cache, reduce) e Lambdas.
Compatível com Python 3.12+.
"""
from __future__ import annotations
from collections.abc import Generator, Iterable
from dataclasses import dataclass
from decimal import Decimal
from functools import lru_cache, partial, reduce
import re
import time
from typing import Final
# ==============================================================================
# 1. MODELO DE LOG E ENRIQUECIMENTO GEOGRÁFICO MEMOIZADO
# ==============================================================================
@dataclass(slots=True)
class EventoLogAcesso:
timestamp: str
ip_origem: str
pais_origem: str
metodo_http: str
url_endpoint: str
status_code: int
latencia_ms: float
bytes_enviados: int
# Memoização automática com LRU Cache: Evita consultas repetidas de IP
@lru_cache(maxsize=1024)
def resolver_pais_por_ip(ip: str) -> str:
"""Simula resolução de ASN / GeoIP com cache em memória."""
if ip.startswith("200.") or ip.startswith("189."):
return "Brasil"
elif ip.startswith("104.") or ip.startswith("54."):
return "Estados Unidos"
elif ip.startswith("140."):
return "Japão"
return "Desconhecido"
# ==============================================================================
# 2. ESTÁGIOS DO PIPELINE DE STREAMING (GERADORES COM YIELD)
# ==============================================================================
def estagio_1_gerar_logs_brutos(linhas_brutas: Iterable[str]) -> Generator[str, None, None]:
"""Lê linhas brutas ignorando linhas em branco ou comentários."""
for linha in linhas_brutas:
linha_limpa = linha.strip()
if linha_limpa and not linha_limpa.startswith("#"):
yield linha_limpa
def estagio_2_parsear_eventos(linhas_stream: Generator[str, None, None]) -> Generator[EventoLogAcesso, None, None]:
"""Converte strings em objetos de domínio tipados."""
padrao_regex = re.compile(
r"^(?P<ts>[\d\-:T]+)\s+(?P<ip>[\d\.]+)\s+(?P<metodo>[A-Z]+)\s+(?P<url>[^\s]+)\s+(?P<status>\d{3})\s+(?P<lat>[\d\.]+)\s+(?P<bytes>\d+)$"
)
for linha in linhas_stream:
match = padrao_regex.match(linha)
if match:
d = match.groupdict()
ip = d["ip"]
yield EventoLogAcesso(
timestamp=d["ts"],
ip_origem=ip,
pais_origem=resolver_pais_por_ip(ip), # Chamada cacheada
metodo_http=d["metodo"],
url_endpoint=d["url"],
status_code=int(d["status"]),
latencia_ms=float(d["lat"]),
bytes_enviados=int(d["bytes"])
)
def estagio_3_filtrar_anomalias(
eventos: Generator[EventoLogAcesso, None, None],
limiar_latencia: float = 200.0
) -> Generator[EventoLogAcesso, None, None]:
"""Filtra requisições lentas ou erros de servidor (HTTP 5xx)."""
for ev in eventos:
if ev.latencia_ms >= limiar_latencia or ev.status_code >= 500:
yield ev
# ==============================================================================
# 3. REDUÇÃO FUNCIONAL COM FUNCTOOLS.REDUCE
# ==============================================================================
@dataclass(slots=True)
class EstatisticasTrafego:
total_requisicoes: int = 0
total_bytes: int = 0
latencia_acumulada: float = 0.0
def acumulador_metricas(acum: EstatisticasTrafego, ev: EventoLogAcesso) -> EstatisticasTrafego:
"""Função pura redutora para agregação estatística."""
acum.total_requisicoes += 1
acum.total_bytes += ev.bytes_enviados
acum.latencia_acumulada += ev.latencia_ms
return acum
# ==============================================================================
# 4. EXECUÇÃO DO PIPELINE
# ==============================================================================
if __name__ == "__main__":
logs_simulados = [
"2026-09-28T10:00:01 200.147.3.15 GET /api/v1/produtos 200 45.2 1250",
"2026-09-28T10:00:02 54.210.88.9 POST /api/v1/checkout 500 450.8 450", # Anomalia: 500 e latência
"2026-09-28T10:00:03 200.147.3.15 GET /api/v1/produtos 200 32.1 1250", # Hit de Cache no GeoIP
"2026-09-28T10:00:04 140.22.90.11 GET /api/v1/status 200 12.0 120",
"2026-09-28T10:00:05 189.40.12.88 POST /api/v1/pagamentos 200 310.5 890", # Anomalia: Latência alta
"2026-09-28T10:00:06 54.210.88.9 GET /api/v1/pedidos 200 55.4 3400",
]
print("=" * 80)
print(" PIPELINE FUNCIONAL EM STREAMING LAZY COM GERADORES E FUNCTOOLS")
print("=" * 80)
# Montagem declarativa do pipeline de streaming
g_bruto = estagio_1_gerar_logs_brutos(logs_simulados)
g_parsed = estagio_2_parsear_eventos(g_bruto)
g_anomalias = estagio_3_filtrar_anomalias(g_parsed, limiar_latencia=200.0)
print(">> Incidentes Críticos Filtrados no Fluxo:")
print(f"{'Horário':<20} | {'Origem':<14} | {'País':<14} | {'Status':<7} | {'Latência':<10}")
print("-" * 80)
lista_anomalias: list[EventoLogAcesso] = []
for alerta in g_anomalias:
lista_anomalias.append(alerta)
print(
f"{alerta.timestamp:<20} | "
f"{alerta.ip_origem:<14} | "
f"{alerta.pais_origem:<14} | "
f"HTTP {alerta.status_code:<2} | "
f"{alerta.latencia_ms:>6.1f} ms"
)
# Redução funcional dos incidentes com reduce
metricas = reduce(acumulador_metricas, lista_anomalias, EstatisticasTrafego())
print("-" * 80)
print(f"Total de Anomalias Registradas : {metricas.total_requisicoes}")
print(f"Volume Trafegado em Falhas : {metricas.total_bytes:,} bytes")
if metricas.total_requisicoes > 0:
media_lat = metricas.latencia_acumulada / metricas.total_requisicoes
print(f"Latência Média dos Incidentes : {media_lat:.2f} ms")
print("-" * 80)
print(f"Telemetria do Cache LRU GeoIP : {resolver_pais_por_ip.cache_info()}")
print("=" * 80)