[PYTHON] Usando funções decoradoras (Decorators)
Saudações!
Recentemente estava eu me aventurando com Flask e notei que poderia ser usado um @login_required junto com alguma rota da aplicação e fiquei curioso sobre o funcionamento
Que tal ver como funcionam esses famosos @'s que podemos adicionar uma linha antes da nossas funções em python?
Bora lá!
Antes de tudo a PEP 318 fala muita coisa sobre decoradores para funções e métodos e ainda tem alguns exmplos bem da hora, vale dar uma olhadinha 😉
Em Python, os decoradores são uma forma poderosa de modificar ou estender o comportamento de funções ou métodos sem alterar seu código interno. 🚀🚀
Eles são aplicados usando a sintaxe @decorador acima da definição da sua função, desse jeito.
@meu_decorador
def minha_função ():
#código
O decorador é, na verdade, uma função que recebe outra função como argumento e retorna uma nova função que geralmente estende ou modifica o comportamento da função original. 🤖
Doido, não?! Relaxa, é muito simples, saca esse exemplo!
# Exemplo de um decorador simples
def meu_decorador(funcao):
def wrapper():
print("Antes da execução da função.")
resultado = funcao()
print("Depois da execução da função.")
return resultado
return wrapper
@meu_decorador
def minha_funcao():
print("Executando minha função.")
# Chamando a função decorada
minha_funcao()
Execução do código:
Antes da execução da função.
Executando minha função.
Depois da execução da função.
Neste exemplo, @meu_decorador é um decorador que envolve a função minha_funcao com funcionalidades adicionais. Quando chamamos minha_funcao(), o decorador é aplicado automaticamente.
O que acontece nos bastidores é que a função minha_funcao é passada como argumento para a função meu_decorador, e o decorador retorna uma nova função chamada wrapper. Essa nova função inclui o código adicional (como por exemplo imprimir mensagens antes e depois da execução da função original) e, em seguida, chama a função original (funcao()).
É importante notar que, ao usar o decorador com @meu_decorador acima da definição da função, você está essencialmente dizendo que deseja que minha_funcao seja envolvida pelo meu_decorador antes de ser chamada.
UAU! certo? mas e se você precisar passar parâmetros?
Você pode fazer isso utilizando os *args e **kwargs na função wrapper e passá-los na chamada da função que foi decorada, simples assim!
# Exemplo de um decorador simples com parâmetros
def meu_decorador(funcao):
def wrapper(*args, **kwargs): # <------ Parâmetros para a função
print("Antes da execução da função.")
resultado = funcao(*args, **kwargs) # <------ Passagem dos parâmetros na chamada da função
print("Depois da execução da função.")
return resultado
return wrapper
@meu_decorador
def minha_funcao(nome):
print("Executando minha função.")
print(f"Olá, {nome}!")
# Chamando a função decorada
minha_funcao("João")
Execução do código:
Antes da execução da função.
Executando minha função.
Olá, João!
Depois da execução da função.
Este é um exemplo básico, mas decoradores podem ter uma variedade de aplicações e serem utilizados para tarefas como verificação de autorização, medição de tempo de execução, entre outros.
O Python possui muitos decoradores incorporados, como @staticmethod, @classmethod, e a biblioteca Flask, assim como outras fazem extenso uso de decoradores para configurar rotas, autenticação, etc.
Exemplos:
TEMPO DE EXECUÇÃO - Mede o tempo de execução da função e faz o log no terminal
import time
def medir_tempo(func):
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = func(*args, **kwargs)
fim = time.time()
print(f"{func.__name__} levou {fim - inicio} segundos para executar.")
return resultado
return wrapper
@medir_tempo
def operacao_demorada():
# Código demorado
time.sleep(2)
return "Concluído."
FLASK - Acesso de rota da aplicação somente com login efetuado, caso não esteja logado redireciona para a página de login.
from functools import wraps
from flask import g, request, redirect, url_for
# Decorador já existente na biblioteca Flask
df login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if g.user is None:
return redirect(url_for('login', next=request.url)) #<---- Redireciona para o login
return f(*args, **kwargs)
return decorated_function
Criação da rota
@app.route('/secret_page')
@login_required #<---- Exige login para acessar rota secreta
def secret_page():
pass
Referência: Documentação Flask
Deixa um like se gostou do conteúdo. ❤️
Usava sem saber direito ( falta de curiosidade e ao mesmo tempo a doc do python é péssima ). Muito obrigado pela explicação.
Muito bom. Uso muito os decoradores para fazer validação de dados também.
Cara tava com uma dificuldade de achar um método agil de colocar um delay no meu jogo em algumas ações e não conseguia pensar em nada,ai lembrei dos decorators quando vi seu post, então dai eu criei decorator que mudou tudo.
Também fiz uma descoberta. Descobri que poosso utilizar uma classe como decorador, que é bem importante para algumas ações, e achei interessante o modo como funciona para utilizar instâncias da classe dentro de um decorador, pois quando utilizamos diretamente na função ele não aceita usarmos a instancia de classe "self", sendo assim a synxtax ficária como a demonstrada abaixo.
import pygame.time
class Timer:
def __init__(self, delay: int):
self.last_update = pygame.time.get_ticks()
self.delay = delay
def __call__(self, func):
def wrapper(*args, **kwargs):
elapsed = pygame.time.get_ticks()
if elapsed - self.last_update > self.delay:
self.last_update = elapsed
return func(*args, **kwargs)
return wrapper
class Player(Entity):
def __init__(self, context, image):
super().__init__(context, image)
....
##########
# TIMERS #
##########
self.move = Timer(delay=self.speed())(self.move)
self.frame_motion = Timer(delay=10 if self.speed() >= 500 else 150 * (1 + (self.speed() / 500)))(
self.frame_motion)
....
incrível!! Muito bem explicado com uma didatica excepcional! Só que numca entendi muito bem o funcionamento de decorares por esse mesmo motivo não usava, mas agora que entendi o seu real poder vou usar bastante, mas poderia explicar o *args
e o **kwargs
? nunca entendi o significado.