Módulo 12 - Decoradores
Casos de uso reais de decoradores
11 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Reconhecer quando um decorador é a ferramenta certa: comportamento repetido em várias funções.
- Escrever um decorador que mede o tempo de execução de uma função.
- Escrever um decorador que registra cada chamada em um log.
- Escrever um decorador que valida argumentos antes de executar a função.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Casos de uso reais de decoradores.
Os objetivos desta aula. Reconhecer quando um decorador é a ferramenta certa: comportamento repetido em várias funções. Escrever um decorador que mede o tempo de execução de uma função. Escrever um decorador que registra cada chamada em um log. Escrever um decorador que valida argumentos antes de executar a função.
Veja o essencial, parte por parte.
Quando o decorador é a escolha certa. Use decorador quando o mesmo comportamento extra se repete em várias funções.
Medir tempo e registrar chamadas. O primeiro decorador cronometra.
Validar argumentos antes de rodar. O terceiro caso é a validação.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Quando o decorador é a escolha certa
Decorador não serve para tudo, e sair espalhando @ por todo lado deixa o código pior. Ele é a ferramenta certa para um caso específico: quando o mesmo comportamento auxiliar precisa envolver muitas funções diferentes, sem ser o assunto principal de nenhuma delas. Pense em medir quanto tempo cada função demora, registrar quem foi chamado e com quais dados, ou conferir se os argumentos são válidos antes de rodar. Escrever isso dentro de cada função geraria repetição e sujeira. Um decorador isola esse comportamento transversal em um lugar só e o aplica com uma linha.
Os três exemplos a seguir usam o mesmo formato completo que você montou ao longo do módulo: função externa recebendo func, wrapper com *args e **kwargs, return do resultado e functools.wraps por cima. Só muda o que acontece dentro do wrapper. Isso reforça a boa notícia do assunto: uma vez entendido o esqueleto, cada novo decorador é só preencher o miolo.
Medir tempo e registrar chamadas
O primeiro decorador cronometra. Ele marca o relógio de alta precisão antes de chamar a função, executa a função guardando o resultado, marca o relógio de novo e mostra a diferença. Usamos time.perf_counter, feito para medir intervalos curtos. O segundo decorador registra: antes de executar, imprime o nome da função e os argumentos recebidos; depois, mostra o que ela devolveu. Em um sistema real, esse print viraria uma gravação no módulo de logging, mas a ideia é a mesma. Note como os dois seguem o formato idêntico, mudando só o miolo.
import functools
import time
def cronometro(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
inicio = time.perf_counter()
resultado = func(*args, **kwargs)
fim = time.perf_counter()
print(f"{func.__name__} levou {fim - inicio:.6f}s")
return resultado
return wrapper
def registrar(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"chamando {func.__name__} args={args} kwargs={kwargs}")
resultado = func(*args, **kwargs)
print(f"{func.__name__} devolveu {resultado!r}")
return resultado
return wrapper
@cronometro
def somar_ate(n):
return sum(range(n))
@registrar
def saudar(nome, saudacao="Olá"):
return f"{saudacao}, {nome}!"
print(somar_ate(1_000_000))
# somar_ate levou 0.0123s (o número varia por máquina)
# 499999500000
saudar("Ana", saudacao="Oi")
# chamando saudar args=('Ana',) kwargs={'saudacao': 'Oi'}
# saudar devolveu 'Oi, Ana!'cronometro mede o tempo; registrar loga entrada e saída. Mesmo formato, miolos diferentes.
Validar argumentos antes de rodar
O terceiro caso é a validação. Aqui o wrapper faz uma checagem antes de deixar a função rodar e, se algo estiver errado, levanta um erro em vez de chamar a função. É útil para blindar funções que só fazem sentido com certos valores, como recusar números negativos onde só cabem positivos. O ponto importante é que a função original fica limpa, cuidando só do cálculo, enquanto a regra de entrada mora no decorador. Se a mesma regra vale para várias funções, você a escreve uma vez e a aplica com um @ em cada uma.
import functools
def exigir_positivos(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for valor in args:
if isinstance(valor, (int, float)) and valor < 0:
raise ValueError(f"{func.__name__} não aceita valores negativos: {valor}")
return func(*args, **kwargs)
return wrapper
@exigir_positivos
def media(a, b):
return (a + b) / 2
print(media(8, 10)) # 9.0
print(media(8, -4))
# ValueError: media não aceita valores negativos: -4O decorador barra argumentos negativos antes de a função rodar; media fica limpa.
Teste rápido
Qual é a vantagem de validar argumentos com um decorador em vez de dentro de cada função?
Perguntas frequentes
- Quando NÃO devo usar um decorador?
- Quando o comportamento é específico de uma única função e não se repete. Nesse caso, escrever a lógica direto na função é mais claro. Decorador compensa quando o mesmo extra (log, tempo, validação, cache) precisa envolver muitas funções. Usar @ por moda, sem essa repetição, só adiciona indireção sem ganho.
- Por que usar time.perf_counter e não time.time?
- O perf_counter é um relógio de alta precisão feito para medir intervalos, imune a ajustes do relógio do sistema. O time.time devolve a hora do dia, com menos precisão para trechos curtos. Para cronometrar execução, perf_counter é a escolha recomendada pela documentação.
- Em produção, uso print no decorador de log?
- Nos exemplos usamos print para ficar simples de ler. Em um programa real, troque por uma chamada ao módulo logging da biblioteca padrão, que permite níveis, formatação e destino configurável. A estrutura do decorador continua idêntica; muda só o que você faz com a informação da chamada.
- O decorador de validação impede a função de rodar?
- Sim, quando encontra um argumento inválido. Ele checa antes e, se algo estiver errado, levanta um erro (como ValueError) sem nunca chamar a função original. Se tudo estiver certo, ele deixa a função rodar normalmente e devolve o resultado. Assim a função só executa com entradas válidas.
- Posso combinar mais de um desses decoradores?
- Pode. Empilhe as linhas @ sobre a mesma função, por exemplo @cronometro e @registrar juntos. Eles se aplicam de baixo para cima e cada um envolve o resultado do outro. Só cuide da ordem, pois ela muda em que sequência os comportamentos acontecem ao redor da função.
- Esses decoradores mudam o resultado da função?
- Não, e é de propósito. cronometro e registrar apenas observam: medem, imprimem e devolvem o mesmo resultado da função original. O de validação pode interromper com um erro, mas quando deixa rodar, devolve o resultado intacto. Decoradores de observação e guarda não devem falsear o valor devolvido.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.