Decorators Avançados e Parametrizados em Python: Padrões Profissionais

Em Pythondecorators são uma poderosa ferramenta de programação que permitem modificar ou estender o comportamento de funções, métodos ou classes sem alterar seu código interno. Eles são amplamente usados em frameworks como FlaskDjango e FastAPI, e dominá-los é essencial para quem busca padrões profissionais de desenvolvimento.


1. Revisão Rápida: Decorators Básicos

Um decorator básico é uma função que recebe outra função como argumento e retorna uma nova função:

def decorator_simples(func):
    def wrapper(*args, **kwargs):
        print("Antes da execução")
        resultado = func(*args, **kwargs)
        print("Depois da execução")
        return resultado
    return wrapper

@decorator_simples
def saudacao(nome):
    print(f"Olá, {nome}!")

saudacao("Ana")

Saída:

Antes da execução
Olá, Ana!
Depois da execução

O decorator intercepta a chamada à função e adiciona lógica antes e depois da execução original.


2. Decorators Avançados

Decorators avançados geralmente envolvem:

  • Uso de *args e **kwargs para funções genéricas.

  • Encadeamento de múltiplos decorators.

  • Preservação da metadata da função com functools.wraps.

  • Aplicação em métodos de classe e estáticos.

2.1 Preservando Metadata com functools.wraps

from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Chamando {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def soma(a, b):
    """Soma dois números."""
    return a + b

print(soma.__name__)  # soma
print(soma.__doc__)   # Soma dois números.
  • Sem @wraps, a função decorada perde seu nome original e docstring, o que prejudica documentação e introspecção.


2.2 Encadeamento de Decorators

É possível aplicar vários decorators a uma função:

def dobro(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return 2 * func(*args, **kwargs)
    return wrapper

@log
@dobro
def numero():
    return 10

print(numero())

Saída:

Chamando numero
20
  • A ordem de aplicação importa: o decorator mais próximo da função é aplicado primeiro.


3. Decorators Parametrizados

Um decorator parametrizado é um decorator que aceita argumentos, permitindo customizar seu comportamento.

3.1 Exemplo Básico

from functools import wraps

def repetir(vezes):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            resultado = None
            for _ in range(vezes):
                resultado = func(*args, **kwargs)
            return resultado
        return wrapper
    return decorator

@repetir(3)
def dizer_ola():
    print("Olá!")

dizer_ola()

Saída:

Olá!
Olá!
Olá!
  • O decorator recebe o parâmetro vezes e cria um decorator interno que aplica a lógica.


3.2 Uso Profissional: Controle de Acesso

Decorators parametrizados são muito úteis em autenticação, autorização e logs customizados.

def permissao(roles_permitidos):
    def decorator(func):
        @wraps(func)
        def wrapper(user, *args, **kwargs):
            if user['role'] not in roles_permitidos:
                raise PermissionError("Acesso negado!")
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

@permissao(['admin', 'moderador'])
def deletar_post(user, post_id):
    print(f"Post {post_id} deletado pelo usuário {user['name']}")

usuario = {'name': 'Ana', 'role': 'admin'}
deletar_post(usuario, 42)
  • Permite controlar dinamicamente o acesso a funções sem poluir o código principal.


4. Decorators para Classes

Além de funções, decorators podem ser aplicados diretamente em classes:

def registrar_classe(cls):
    print(f"Registrando classe {cls.__name__}")
    return cls

@registrar_classe
class Produto:
    pass
  • Útil para frameworks, plugins e automação de registro de classes.


5. Decorators Avançados com Padrões Profissionais

5.1 Memoization (Cache)

def memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@memoize
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(35))  # Calcula de forma eficiente
  • Evita recalcular resultados de funções puras, aumentando performance.

5.2 Logging e Auditoria

def auditar(acao):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(f"Ação: {acao}, função: {func.__name__}, args: {args}, kwargs: {kwargs}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@auditar("Atualizar estoque")
def atualizar_estoque(produto, quantidade):
    print(f"{quantidade} unidades de {produto} atualizadas")

atualizar_estoque("Notebook", 5)

6. Boas Práticas

  1. Use functools.wraps sempre para preservar metadata.

  2. Evite lógica pesada no decorator — delegue ao código principal ou helper functions.

  3. Prefira decorators parametrizados para aumentar flexibilidade.

  4. Combine com classes, mixins e metaclasses para sistemas avançados.

  5. Documente bem — decorators podem confundir se aplicados em cascata.

  6. Teste decoradores isoladamente — erros podem ser difíceis de rastrear.


7. Conclusão

  • Decorators avançados e parametrizados transformam funções simples em ferramentas poderosas, aplicáveis a logging, validação, caching, autorização e muito mais.

  • O uso de encadeamento de decorators, functools.wraps e parametrização é fundamental para padrões profissionais de Python.

  • Combinando decorators com POO avançada, mixins e metaclasses, você consegue criar frameworks e sistemas robustos e altamente reutilizáveis.

Comentários

Postagens mais visitadas deste blog

Laços de Repetição em Python: Conceitos e Exemplos Práticos

Manipulação de Arquivos no C#: Como Ler, Escrever e Trabalhar com Arquivos de Forma Simples

Como Instalar o Xamarin com C#: Passo a Passo Completo