Decorators em Python

python
0 – Python
8 – Orientada a Aspectos (AOP)
8.1 – Decorators para logging, cache, autenticação
8.2 – Monkey patching
8.3 – Descriptors (__get__, __set__)
LEGENDA
Nivel_1
Nivel_2
Nivel_3

Decorators são funções que modificam o comportamento de outras funções. Eles representam um recurso elegante da linguagem Python. Com um decorator, você adiciona funcionalidades sem alterar o código original. Isso promove o princípio DRY (Don’t Repeat Yourself). Além disso, eles resolvem problemas comuns de logging, cache e autenticação. Cada decorator atende uma necessidade específica de forma limpa. Por essa razão, eles se tornaram ferramentas indispensáveis. Assim, qualquer desenvolvedor Python deveria dominá-los.

Decorator para logging: rastreando execuções

O logging registra quando uma função é chamada e seus argumentos. Ele também captura o tempo de execução ou possíveis erros. Portanto, você obtém uma trilha útil para depuração em produção. Geralmente, aplicamos logging em funções críticas ou em APIs. Por exemplo, você pode logar todas as requisições a um banco. O decorator elimina a repetição manual de prints. Consequentemente, a manutenção do código fica muito mais simples. Além disso, você pode ativar ou desativar logs globalmente.

Uma fórmula mental para logging é:

\(\text{Log}(f) = \text{registro}(\text{entrada}) \rightarrow f \rightarrow \text{registro}(\text{saída})\)
Esse padrão anota algo antes e depois da função. Assim, você cria uma trilha completa de execução. Evite logar dados sensíveis como senhas ou tokens. Use níveis diferentes (INFO, DEBUG, ERROR) para organizar. Dessa maneira, o log fica útil sem poluir a saída.

Decorator para cache: acelerando repetições

O cache armazena resultados de funções com os mesmos argumentos. Quando a mesma chamada ocorre novamente, retornamos o valor imediato. Isso poupa tempo computacional em cálculos pesados. Operações de rede ou consultas lentas se beneficiam enormemente. Por outro lado, evite cache em funções que alteram estado externo. Um exemplo clássico é o cálculo recursivo de Fibonacci. Sem cache, sua complexidade se torna exponencial. Com cache, a complexidade cai para linear. Portanto, o cache é uma otimização poderosa.

A lógica do cache pode ser expressa assim:

\(\text{Cache}(f)(x) = \begin{cases} f(x) & \text{se } x \notin \text{memo} \\ \text{memo}[x] & \text{caso contrário} \end{cases}\)
Python já oferece functools.lru_cache para essa tarefa. Use cache quando sua função for pura e repetitiva. Primeiro, identifique gargalos de desempenho no seu sistema. Depois, adicione o decorator de cache nessas funções. Assim, você ganha velocidade sem alterar a lógica original.

Decorator para autenticação: protegendo acesso

A autenticação verifica se o usuário possui permissão antes da execução. Esse decorator aparece frequentemente em frameworks web como Flask. Ele pode checar um token, uma sessão ativa ou um papel. Se a verificação falhar, o decorator lança uma exceção. Dessa forma, o sistema impede ações não autorizadas. A lógica de segurança fica centralizada em um único local. Além disso, você nunca deve confiar apenas no front-end. A validação no back-end continua sendo obrigatória. Portanto, decorators de autenticação são indispensáveis.

Por exemplo, rotas administrativas ou exclusão de dados exigem papel admin. A função original roda exclusivamente para usuários autorizados. Caso contrário, o decorator retorna uma mensagem de erro. Esse padrão é amplamente adotado em sistemas reais. Sua clareza facilita auditorias de segurança. Ademais, você pode compor múltiplos decorators de autenticação. Isso permite criar hierarquias de permissões sofisticadas. Primeiro, defina os papéis disponíveis no sistema.

Código completo dos três decorators em python

Abaixo, apresentamos um exemplo funcional com logging, cache e autenticação. Cada decorator aparece separadamente para facilitar o entendimento. Observe como aplicamos eles com o símbolo @. A ordem dos decorators influencia o comportamento final. Primeiro, avalie qual camada deve executar primeiro. Geralmente, autenticação vem antes de logging. Cache geralmente fica na camada mais interna. Teste cada combinação para evitar surpresas indesejadas.

Neste código, o logging mostra cada chamada e seu respectivo tempo. O cache evita recálculos desnecessários da sequência de Fibonacci. A autenticação impede ações de usuários não autorizados. Primeiro, observe que as funções originais permanecem simples. Elas focam exclusivamente em sua tarefa principal. As preocupações transversais ficaram isoladas nos decorators. Essa abordagem segue as boas práticas da comunidade Python. Além disso, decorators empilhados executam de baixo para cima. Portanto, preste atenção na ordem ao combiná-los. Finalmente, teste sempre cada decorator individualmente. Isso garante que eles funcionem como você espera. Assim, você constrói sistemas mais robustos e organizados.

Orientação a Aspectos (AOP)

python
0 – Python
8 – Orientada a Aspectos (AOP)
8.1 – Decorators para logging, cache, autenticação
8.2 – Monkey patching
8.3 – Descriptors (__get__, __set__)
LEGENDA
Nivel_1
Nivel_2
Nivel_3

A Programação Orientada a Aspectos (AOP) separa preocupações transversais do código principal. Essas preocupações incluem logging, segurança ou transações. Muitas vezes, elas se espalham por vários módulos. Por exemplo, o mesmo log é repetido em camadas diferentes. Isso gera código emaranhado e difícil de manter. A AOP resolve isso com a modularização de aspectos.

Originalmente popularizada pelo Spring Framework e AspectJ, a AOP também existe em Python. A biblioteca aspectlib ou decoradores personalizados são exemplos práticos. A ideia central é separar o “o quê” (regras de negócio) do “quando” (cross-cuttings). Consequentemente, a coesão do sistema aumenta visivelmente. Assim, desenvolvedores focam na lógica principal sem distrações.

Conceitos fundamentais da Aop

Existem quatro elementos-chave: join point, pointcut, advice e aspecto. Join point é um ponto na execução do programa, como uma chamada de método. Pointcut é uma expressão que seleciona um conjunto de join points. Advice é a ação executada antes, depois ou ao redor do pointcut. Finalmente, o aspecto une pointcut com advice. Foi dito que isso lembra a meta-programação, mas com foco maior em separação.

A relação entre essas partes pode ser expressa assim:

\(\text{Aspecto} = \text{Pointcut} \land \text{Advice}\)
Isso significa que o aspecto só age quando o pointcut é verdadeiro. Por outro lado, sem um pointcut bem definido, os advices podem ser aplicados incorretamente. Comportamento inesperado é evitado com testes específicos. Portanto, essa estrutura garante previsibilidade.

Quando utilizar aop no seu projeto

Use AOP quando repetir o mesmo código em muitos lugares. Logging de entrada/saída de métodos é um caso clássico. Controle de acesso ou autorização também se beneficia muito. Gerenciamento de transações em bancos de dados é outro exemplo comum. Medição de desempenho (timers) foi aplicado com sucesso por várias equipes. Essas tarefas não pertencem à lógica de negócio principal. Por isso, elas são chamadas de “preocupações transversais”.

Além disso, evite AOP para fluxos de negócio complexos. Isso pode tornar o código obscuro e difícil de depurar. De fato, a rastreabilidade é reduzida quando muitos aspectos são usados. Então, recomenda-se começar com poucos aspectos bem documentados. Uma boa prática é limitar aspectos a infraestrutura ou segurança. Assim, você mantém a legibilidade e o benefício real da AOP.

Exemplo prático em python

O código abaixo mostra um aspecto simples de logging. Ele intercepta qualquer função com o decorador @log_execution. Antes da execução, imprime o nome e argumentos. Depois, mostra o tempo gasto pela função. Essa técnica centraliza o logging em um único lugar. Isso é muito melhor do que copiar print em 50 funções. Observe como a função de negócio (soma) fica limpa.

No exemplo, nenhuma função contém código de log. As preocupações transversais foram isoladas no decorador. Isso é um padrão AOP simplificado para Python. Caso precisasse de pointcuts mais complexos, use aspectlib ou weaver. A manutenção se torna incrivelmente mais fácil. Você pode adicionar ou remover logs sem tocar nas funções. Por último, lembre-se de que nem tudo deve ser aspecto. Use com sabedoria, e seu código será mais elegante.