Decorators em Python: o que são e como criar o seu
Entenda de verdade como funciona a mágica por trás do @login_required.
O que é um decorator?
Um decorator é uma função que recebe outra função como argumento e retorna uma nova função com comportamento adicional. Em Python, usamos o símbolo @ como atalho sintático para aplicar decorators.
Na prática, quando você escreve:
@meu_decorator
def minha_funcao():
pass
O Python está fazendo isso por baixo dos panos:
minha_funcao = meu_decorator(minha_funcao)
Funções são objetos
Para entender decorators, você precisa saber que funções em Python são objetos de primeira classe. Isso significa que você pode passar funções como argumentos, retorná-las de outras funções e atribuí-las a variáveis.
def saudacao(nome):
return f"Ola, {nome}!"
# Atribuindo a funcao a uma variavel
cumprimento = saudacao
print(cumprimento("Maria")) # Ola, Maria!
# Passando como argumento
def executar(funcao, valor):
return funcao(valor)
print(executar(saudacao, "Joao")) # Ola, Joao!
Criando seu primeiro decorator
Vamos criar um decorator que mede o tempo de execução de uma função:
import time
from functools import wraps
def medir_tempo(funcao):
@wraps(funcao)
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = funcao(*args, **kwargs)
fim = time.time()
print(f"{funcao.__name__} executou em {fim - inicio:.4f}s")
return resultado
return wrapper
@medir_tempo
def buscar_dados():
time.sleep(1)
return {"status": "ok"}
buscar_dados()
# buscar_dados executou em 1.0012s
O @wraps(funcao) é importante porque preserva o nome e a docstring da função original. Sem ele, buscar_dados.__name__ retornaria "wrapper" em vez de "buscar_dados".
Decorators com argumentos
Quando o decorator precisa receber parâmetros, você adiciona mais uma camada de função:
from functools import wraps
def repetir(vezes):
def decorator(funcao):
@wraps(funcao)
def wrapper(*args, **kwargs):
for _ in range(vezes):
resultado = funcao(*args, **kwargs)
return resultado
return wrapper
return decorator
@repetir(vezes=3)
def dizer_ola():
print("Ola!")
dizer_ola()
# Ola!
# Ola!
# Ola!
Como funciona o @login_required
Agora que você entende decorators, fica fácil compreender o @login_required do Flask-Login. Ele segue a mesma lógica:
from functools import wraps
from flask import redirect, url_for
from flask_login import current_user
def login_required(funcao):
@wraps(funcao)
def wrapper(*args, **kwargs):
if not current_user.is_authenticated:
return redirect(url_for("login"))
return funcao(*args, **kwargs)
return wrapper
Antes de executar a função da rota, o decorator verifica se o usuário está autenticado. Se não estiver, redireciona para a página de login. Caso contrário, a função original é executada normalmente.
Exemplo prático: decorator de permissão
Vamos criar um decorator que verifica se o usuário tem uma permissão específica:
from functools import wraps
from flask import abort
from flask_login import current_user
def requer_permissao(permissao):
def decorator(funcao):
@wraps(funcao)
def wrapper(*args, **kwargs):
if not current_user.tem_permissao(permissao):
abort(403)
return funcao(*args, **kwargs)
return wrapper
return decorator
@app.route("/admin")
@login_required
@requer_permissao("admin")
def painel_admin():
return render_template("admin.html")
Conclusão
Decorators são uma ferramenta poderosa do Python que permite adicionar comportamento a funções sem modificar seu código. Eles estão por toda parte: no Flask, no Django, em bibliotecas de teste e muito mais. Dominar esse conceito vai te ajudar a escrever código mais limpo e reutilizável.