Decoradores em Python são ferramentas poderosas que permitem modificar ou estender o comportamento de funções ou classes sem alterar permanentemente seu código-fonte. Estruturalmente, um decorador é uma função que recebe outra função como argumento e retorna uma nova função com funcionalidades adicionadas, utilizando conceitos de clausuras (closures) e escopos de nomes.
Estrutura Básica de um Decorador
A forma mais simples de um decorador envolve envolver uma função original dentro de uma função interna que executa lógica adicional antes ou depois da chamada principal.
import time
def monitorar_tempo(funcao_alvo):
def wrapper():
inicio = time.time()
funcao_alvo()
fim = time.time()
print(f"Tempo de execução: {fim - inicio:.4f} segundos")
return wrapper
def carregar_sistema():
time.sleep(1.5)
print("Sistema carregado com sucesso.")
# Aplicando manualmente
sistema_com_timer = monitorar_tempo(carregar_sistema)
sistema_com_timer()
Suporte a Argumentos e Retornos
Para que um decorador seja genérico e funcione com qualquer função, ele deve ser capaz de aceitar argumentos variáveis (*args e **kwargs) e capturar o valor de retorno da função original.
import time
def cronometro(funcao):
def executor(*args, **kwargs):
print(f"Iniciando: {funcao.__name__}")
instante_inicial = time.time()
# Captura o retorno da função original
resultado = funcao(*args, **kwargs)
instante_final = time.time()
print(f"Finalizado em: {instante_final - instante_inicial:.4f}s")
return resultado
return executor
def saudar_usuario(nome):
time.sleep(1)
return f"Olá, {nome}! Bem-vindo ao portal."
# Aplicando o decorador com suporte a argumentos
saudar_decorado = cronometro(saudar_usuario)
mensagem = saudar_decorado("Gabriel")
print(mensagem)
Açúcar Sintático com @
O Python oferece uma sintaxe simplificada para aplicar decoradores utilizando o símbolo @ acima da definição da função. Isso substitui a necessidade de reatribuir a função manualmente.
def verificar_status(funcao):
def wrapper(*args, **kwargs):
print("Verificando permissões de acesso...")
return funcao(*args, **kwargs)
return wrapper
@verificar_status
def deletar_registro(id_registro):
print(f"Registro {id_registro} removido.")
deletar_registro(505)
Empilhamento de Decoradores
É possível aplicar múltiplos decoradores a uma única função. Eles são processados de baixo para cima (do mais próximo da função para o mais distante).
def negrito(funcao):
def wrapper():
return f"<b>{funcao()}</b>"
return wrapper
def italico(funcao):
def wrapper():
return f"<i>{funcao()}</i>"
return wrapper
@negrito
@italico
def formatar_texto():
return "Texto Formatado"
print(formatar_texto()) # Saída: <b><i>Texto Formatado</i></b>
Decoradores com Parâmetros Próprios
Quando o próprio decorador precisa de argumentos para configurar seu comportamento, é necessária uma camada aidcional de funções (uma função que retorna o decorador).
def nivel_acesso(nivel):
def decorador_real(funcao_alvo):
def wrapper(*args, **kwargs):
if nivel == "admin":
print("Acesso total concedido.")
return funcao_alvo(*args, **kwargs)
elif nivel == "user":
print("Acesso limitado concedido.")
return funcao_alvo(*args, **kwargs)
else:
print("Acesso negado.")
return wrapper
return decorador_real
@nivel_acesso(nivel="admin")
def painel_controle():
print("Exibindo configurações do servidor.")
painel_controle()
Template Universal de Decoradores
Para a maioria dos casos de uso em desenvolvimento de software, a estrutura a seguir serve como base robusta:
def decorador_padrao(funcao):
def wrapper(*args, **kwargs):
# Lógica antes da execução
resultado = funcao(*args, **kwargs)
# Lógica após a execução
return resultado
return wrapper