
Decorators são funções que envolvem outras funções, adicionando funcionalidades extras sem modificar o código original. Eles são como “enfeites mágicos” que reutilizamos para economizar tempo e deixar nosso código mais limpo.
Por Que Decorators Importam?
Se você está começando sua jornada com Python, certamente já se deparou com a necessidade de realizar a mesma tarefa repetidamente em diferentes partes do seu código. Talvez seja registrar o tempo de execução de uma função ou verificar se um usuário tem permissão antes de executar uma ação. É aqui que os decorators (decoradores) brilham, sendo uma das ferramentas mais elegantes e poderosas da linguagem.
Pense nos decorators como um serviço de personalização para suas funções. Imagine que você tem um bolo (sua função original). Você pode adicionar uma cobertura de chocolate, um granulado ou uma cereja (as funcionalidades extras) sem precisar refazer o bolo inteiro. Essa capacidade de adicionar comportamento de forma modular, limpa e reutilizável é essencial para escrever código Python profissional.
Como Criar e Usar um Decorator em Python
Para entender como criar e usar um decorator, precisamos saber como o Python lida com as funções. Em Python, as funções são consideradas cidadãos de primeira classe.
O que isso quer dizer? Significa que podemos tratar as funções exatamente como tratamos outros tipos de dados, como números ou textos. Por exemplo, você pode:
- Atribuí-las a variáveis.
- Passá-las como argumentos para outras funções.
- Retorná-las como resultado de outras funções.
📦 A Receita do Decorator: Uma Função Dentro da Outra
Um decorator é, na sua essência, uma função que recebe outra função como argumento, adiciona alguma lógica e, em seguida, retorna uma nova versão dessa função.
Vamos desmembrar a estrutura básica:
- Função Externa (O Decorator): Recebe a função original (
func) que queremos decorar. - Função Interna (O “Wrapper”): É a função que será realmente executada. Ela contém a lógica extra que você quer adicionar (o “enfeite”). É importante que esta função interna chame a função original (
func). - Retorno: A função externa retorna a função interna (
wrapper).
🚀 Passo a Passo com um Exemplo Simples
Vamos criar um decorator que simplesmente imprime uma mensagem antes e depois de executar qualquer função, como um registro de atividades.
# 1. Função externa (o decorator)
def meu_decorator(funcao_original):
# 2. Função interna (o wrapper)
# Garante que o wrapper possa receber argumentos (*args, **kwargs)
def wrapper(*args, **kwargs):
print(f"--- Executando a função: {funcao_original.__name__} ---")
# Chama a função original e guarda o resultado
resultado = funcao_original(*args, **kwargs)
print(f"--- Função {funcao_original.__name__} finalizada ---")
return resultado # Retorna o resultado da função original
# 3. Retorno
return wrapper
🏷️ Como Aplicar (O Açúcar Sintático)
A mágica acontece com o símbolo @ (o chamado açúcar sintático). Em vez de fazer uma atribuição manual, usamos o @nome_do_decorator logo acima da definição da função que queremos decorar.
# Aplicando o decorator "meu_decorator" à função "dizer_ola"
@meu_decorator
def dizer_ola(nome):
"""Uma função simples que diz olá."""
print(f"Olá, {nome}!")
return f"Saudação para {nome}"
# Usando a função decorada
valor_retornado = dizer_ola("Dev Explica")
print(f"O valor_retornado é: {valor_retornado}")
# Saída:
# --- Executando a função: dizer_ola ---
# Olá, Dev Explica!
# --- Função dizer_ola finalizada ---
#
# O valor_retornado é: Saudação para Dev Explica
O que o @meu_decorator faz é, na verdade, uma simplificação do seguinte:
# dizer_ola = meu_decorator(dizer_ola)
Ou seja, a função dizer_ola original é passada para o meu_decorator, e o resultado (que é a função wrapper) é reatribuído ao nome dizer_ola. Toda vez que chamarmos dizer_ola(), estaremos chamando o wrapper decorado.
Exemplo Prático: Medindo o Tempo ⏱️
Um caso de uso muito comum para um decorator é medir o tempo de execução de uma função, o que é fundamental para otimização de código.
import time
def medir_tempo(func):
"""
Decorator para medir o tempo de execução de uma função.
"""
def wrapper(*args, **kwargs):
inicio = time.time()
# Chama a função original
resultado = func(*args, **kwargs)
fim = time.time()
tempo_total = fim - inicio
print(f"⌛️ A função {func.__name__} levou {tempo_total:.4f} segundos para executar.")
return resultado
return wrapper
@medir_tempo
def simular_processamento_pesado(n):
"""Simula um cálculo demorado."""
soma = 0
for i in range(n):
soma += i
return soma
# Chamada da função. O decorator fará a medição automaticamente.
resultado_processamento = simular_processamento_pesado(1000000)
# Saída (O tempo de execução será impresso antes do resultado)
# ⌛️ A função simular_processamento_pesado levou 0.0578 segundos para executar.
⚠️ Erros Comuns / Armadilhas
- Esquecer de Retornar a Função Interna: O decorator deve retornar a função
wrapper(a interna), não a execução dela (ou seja, apenas o nome da função sem os parenteses). - Não Usar
*argse**kwargs: Se a sua função decorada aceitar argumentos, owrappersempre deve usar*argse**kwargspara garantir que os argumentos sejam repassados corretamente. - Perda de Metadados: Ao decorar uma função, o nome, a documentação (
docstring) e os metadados da função original são perdidos, sendo substituídos pelos da funçãowrapper. Para corrigir isso, use o decorator@functools.wrapsdo Python no seuwrapper.
✅ Boas Práticas / Dicas Rápidas
- Use
@functools.wraps: Sempre use@functools.wraps(func)no seuwrapperinterno para preservar o nome e adocstringda função original. É uma dica de ouro para depuração! - Nomeie o Decorator de Forma Clara: O nome deve descrever o que ele adiciona (ex:
verificar_permissao,medir_tempo). - Mantenha-o Simples: Um decorator deve ter uma única responsabilidade. Se precisar de lógica complexa, crie vários decorators e aplique-os em camadas.
Conclusão: O Poder da Reutilização
Os decorators em Python são a prova de que pequenos ajustes podem gerar grandes impactos na qualidade do seu código. Eles promovem a reutilização de código e seguem o princípio de programação “Não se Repita” (DRY).
Agora que você entendeu o conceito, te desafio a criar o seu próprio decorator, talvez um que verifique se o input de uma função é um número positivo.
Para quem busca ser um especialista Python, o livro Curso Intensivo de Python: uma Introdução Prática e Baseada em Projetos à Programação ensina python com projetos práticos que resolvem problemas reais. Ao adquirir o livro através deste link, você não só investe no seu conhecimento, como também apoia o blog Dev Explica, ajudando a manter a produção de conteúdo didático e de qualidade!
Compre aqui “Curso Intensivo de Python: uma Introdução Prática e Baseada em Projetos à Programação”