Decoradores: Um bom jeito de alterar o funcionamento das funções, sem realmente alterá-las.
Uma forma de adicionar comportamentos de funções e classes.
O que são Decoradores?
São funções que adicionam comportamentos às funções. Por exemplo: você quer que algo seja apresentado na tela antes da execução de sua função ou após a execução da mesma. Ou então, calcular o tempo de execução da função. Há muitas utilidades no uso de decoradores que talvez você já utiliza e nem sabe.
Quando você cria uma classe e ao criar um método de classe você usa o decorado: @classmethod, ou para marcar propriedades da classe e usa o decorador: @property, olha os decoradores ai. Nestes casos utilizando decoradores para atributos e métodos. Há também como usar os decoradores para toda a classe. Interessente, não é?
Uma coisa que até hoje eu não sabia é que você pode usar decoradores em funções sem argumentos, com argumentos e o mais interessante, você pode criar um decorador que funciona ao mesmo tempo em funções com e sem argumentos: 😮.
Como assim?
Imagine que você quer saber quando uma função é chamada, quando ela termina de ser executada e quanto tempo ela levou para ser executada.
para isso usamos a decoradora abaixo:
import time
def log_execucao(func):
def wrapper(*args, **kwargs):
nome_funcao = func.__name__
print(f"--- [LOG] Iniciando '{nome_funcao}' ---")
inicio_tempo = time.perf_counter() # Marca o tempo de início
#chama função original com todos os seus argumentos
resultado = func(*args, **kwargs)
fim_tempo = time.perf_counter()
tempo_decorrido = fim_tempo - inicio_tempo
print(f"--- [LOG] Finalizando '{nome_funcao}' em {tempo_decorrido:.4f} segundos ---")
return resultado
return wrapper
# Aplicando o decorador
### Função sem argumentos
@log_execucao
def cumprimentar():
print('Olá, mundo!')
print("Chamando a função 'cumprimentar' (sem argumentos)")
cumprimentar()
print('\n')
### Função com argumentos posicionais
@log_execucao
def somar(a, b):
print(f"Calculando {a} + {b}")
time.sleep(0.5) # Somente pra simular um processamento
return a + b
print(f"Chamando a função 'Somar' (com agumentos posicionais)")
soma_resultado = somar(10, 20)
print(f"Resultado da soma: {soma_resultado}")
print('\n')
### Função com Argumentos Nomeados
@log_execucao
def configurar(host='localhost', port=8080):
print(f'COnfigurando servidor em: {host}:{port}...')
time.sleep(0.2)
return f"Serviço configurado para {host}:{port}"
print(f'Chamando a função "Configurar" (com argumentos nomeados):')
configuracao_resultado = configurar(port=3000, host='meu_servidor.com')
print(f"Status de configuração: {configuracao_resultado}")
print('\n')
### Função com ambos os tipos de argumentos
@log_execucao
def calcular_desconto(preco_base, percentual, cupom=None):
desconto_valor = preco_base * (percentual / 100)
preco_final = preco_base - desconto_valor
if cupom:
print(f'Aplicando cumpo: {cupom}...')
print(f"Preço original: R${preco_base:.2f}, Desconto: {percentual}%, Preço final {preco_final:.2f}")
return preco_final
print('Chamando a função "calcular_desconto" (com argumentos posicionais e nomeados)')
preco_descontado = calcular_desconto(100.00, 10, cupom='PRIMEIRACOMPRA')
print(f"Preço após desconto: R${preco_descontado:.2f}")
# Saídas
# Chamando a função 'cumprimentar' (sem argumentos)
# --- [LOG] Iniciando 'cumprimentar' ---
# Olá, mundo!
# --- [LOG] Finalizando 'cumprimentar' em 0.0001 segundos ---
# Chamando a função 'Somar' (com agumentos posicionais)
# --- [LOG] Iniciando 'somar' ---
# Calculando 10 + 20
# --- [LOG] Finalizando 'somar' em 0.5005 segundos ---
# Resultado da soma: 30
# Chamando a função "Configurar" (com argumentos nomeados):
# --- [LOG] Iniciando 'configurar' ---
# COnfigurando servidor em: meu_servidor.com:3000...
# --- [LOG] Finalizando 'configurar' em 0.2002 segundos ---
# Status de configuração: Serviço configurado para meu_servidor.com:3000
# Chamando a função "calcular_desconto" (com argumentos posicionais e nomeados)
# --- [LOG] Iniciando 'calcular_desconto' ---
# Aplicando cumpo: PRIMEIRACOMPRA...
# Preço original: R$100.00, Desconto: 10%, Preço final 90.00
# --- [LOG] Finalizando 'calcular_desconto' em 0.0002 segundos ---
# Preço após desconto: R$90.00
Existe também a possibilidade de o próprio decorador receber um argumento.
Imagina que neste decorador log_execucao, queiramos receber a data e o horário Da chamada da função. Então você pode receber o argumento na função externa do decorador.
Olha como fica a funçao log_execucao:
from datetime import datetime
def log_execucao(data):
def decorador_interno(func):
def wrapper(*args, **kwargs):
nome_funcao = func.__name__
print(f"Data e hora da chamada: {data.strftime('%d/%m/%Y %H:%M:%S')}")
print(f"--- [LOG] Iniciando '{nome_funcao}' ---")
inicio_tempo = time.perf_counter() # Marca o tempo de início
#chama função original com todos os seus argumentos
resultado = func(*args, **kwargs)
fim_tempo = time.perf_counter()
tempo_decorrido = fim_tempo - inicio_tempo
print(f"--- [LOG] Finalizando '{nome_funcao}' em {tempo_decorrido:.4f} segundos ---")
return resultado
return wrapper
return decorador_interno
# Criando a função decorada
@log_execucao(datetime.now()) # Agora adicionamos argumento no decorador
def cumprimentar():
print('Olá, mundo!')
print("Chamando a função 'cumprimentar' (sem argumentos)")
cumprimentar()
print('\n')
# SAÍDA
# Chamando a função 'cumprimentar' (sem argumentos)
# Data e hora da chamada: 03/07/2025 17:56:25
# --- [LOG] Iniciando 'cumprimentar' ---
# Olá, mundo!
# --- [LOG] Finalizando 'cumprimentar' em 0.0001 segundos ---
Espero que meus exemplos ajudem a entender um pouco mais os decoradores de funções.