Os decoradores Python são um dos recursos mais elegantes da linguagem. Em 2026, os decoradores estão por toda parte – desde definições de rotas FastAPI até classes de dados, desde verificação de tipo até cache e lógica de repetição. Este guia completo leva você desde o básico até a construção de seus próprios decoradores de nível de produção.
📋 Table of Contents
O que é um decorador?
Um decorador é uma função que envolve outra função para adicionar comportamento. O@sintaxe é açúcar sintático:
# These two are equivalent:
@my_decorator
def my_function():
pass
# Same as:
def my_function():
pass
my_function = my_decorator(my_function)
# Simple decorator example
def log_calls(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"Done {func.__name__}")
return result
return wrapper
@log_calls
def greet(name: str) -> str:
return f"Hello, {name}!"
greet("Alice")
# Calling greet
# Done greet
functools.wraps — Preservar metadados
import functools
def my_decorator(func):
@functools.wraps(func) # preserves __name__, __doc__, __annotations__
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def add(a: int, b: int) -> int:
return a + b
print(add.__name__) # "add" (without wraps, would be "wrapper")
print(add.__doc__) # preserved docstring
Decorador com argumentos
import functools, time
# Decorator that accepts arguments uses three levels of nesting
def retry(max_attempts: int = 3, delay: float = 1.0, exceptions: tuple = (Exception,)):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_error = None
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except exceptions as e:
last_error = e
if attempt < max_attempts:
print(f"Attempt {attempt} failed: {e}. Retrying in {delay}s...")
time.sleep(delay)
raise last_error
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5, exceptions=(ConnectionError, TimeoutError))
def fetch_data(url: str) -> dict:
import requests
return requests.get(url, timeout=5).json()
Decoradores baseados em classe
import functools
import time
from collections import defaultdict
class RateLimit:
def __init__(self, calls: int, period: float):
self.calls = calls
self.period = period
self._call_times: list[float] = []
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
now = time.time()
# Remove calls older than period
self._call_times = [t for t in self._call_times if now - t < self.period]
if len(self._call_times) >= self.calls:
wait_time = self.period - (now - self._call_times[0])
if wait_time > 0:
time.sleep(wait_time)
self._call_times.append(time.time())
return func(*args, **kwargs)
return wrapper
@RateLimit(calls=5, period=1.0) # max 5 calls per second
def api_request(endpoint: str) -> dict:
import requests
return requests.get(f"https://api.example.com{endpoint}").json()
Decoradores de cache
from functools import lru_cache, cache
import time
# Simple memoization (Python 3.9+)
@cache # unlimited cache size
def fibonacci(n: int) -> int:
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# LRU cache with size limit
@lru_cache(maxsize=128)
def expensive_computation(x: float, y: float) -> float:
time.sleep(0.1) # simulate slow computation
return x ** 2 + y ** 2
# Cache with expiry (custom implementation)
def ttl_cache(ttl_seconds: float):
def decorator(func):
cache_store: dict = {}
cache_times: dict = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, tuple(sorted(kwargs.items())))
now = time.time()
if key in cache_store and (now - cache_times[key]) < ttl_seconds:
return cache_store[key] # return cached
result = func(*args, **kwargs)
cache_store[key] = result
cache_times[key] = now
return result
return wrapper
return decorator
@ttl_cache(ttl_seconds=60)
def get_exchange_rate(currency: str) -> float:
# Fetches rate from API, cached for 60 seconds
...
# Inspect cache stats
print(fibonacci.cache_info()) # CacheInfo(hits=5, misses=10, maxsize=None, currsize=10)
fibonacci.cache_clear()
Decoradores em FastAPI
from fastapi import FastAPI, Depends, HTTPException, Request
from functools import wraps
import time
app = FastAPI()
# FastAPI uses decorators for routing
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"id": user_id}
# Custom decorator for timing API endpoints
def measure_time(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start = time.perf_counter()
response = await func(*args, **kwargs)
duration = time.perf_counter() - start
print(f"{func.__name__}: {duration*1000:.2f}ms")
return response
return wrapper
@app.get("/expensive")
@measure_time
async def expensive_endpoint():
import asyncio
await asyncio.sleep(0.1)
return {"status": "done"}
# Require auth decorator
def require_auth(func):
@wraps(func)
async def wrapper(request: Request, *args, **kwargs):
token = request.headers.get("Authorization", "").replace("Bearer ", "")
if not token or not validate_token(token):
raise HTTPException(status_code=401, detail="Unauthorized")
return await func(request, *args, **kwargs)
return wrapper
Padrões comuns de decoradores
import functools, time, logging
logger = logging.getLogger(__name__)
# Timing decorator
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
logger.debug(f"{func.__name__} took {elapsed*1000:.2f}ms")
return result
return wrapper
# Validation decorator
def validate_positive(func):
@functools.wraps(func)
def wrapper(value: float, *args, **kwargs):
if value < 0:
raise ValueError(f"Expected positive number, got {value}")
return func(value, *args, **kwargs)
return wrapper
# Singleton decorator
def singleton(cls):
instances = {}
@functools.wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class DatabaseConnection:
def __init__(self, url: str):
self.url = url
# Deprecation warning
def deprecated(reason: str = ""):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
import warnings
msg = f"{func.__name__} is deprecated. {reason}"
warnings.warn(msg, DeprecationWarning, stacklevel=2)
return func(*args, **kwargs)
return wrapper
return decorator
@deprecated("Use new_function() instead")
def old_function():
pass
Decoradores de empilhamento
# Decorators apply bottom-to-top
@retry(max_attempts=3)
@timer
@validate_positive
def compute(value: float) -> float:
return value ** 2
# Equivalent to:
compute = retry(max_attempts=3)(timer(validate_positive(compute)))
Os decoradores Python são a maneira mais limpa de adicionar preocupações transversais, como registro, cache, limitação de taxa, autenticação e lógica de nova tentativa, sem modificar a função principal. Sempre use@functools.wrapspara preservar metadados. Crie decoradores parametrizados quando precisar de um comportamento configurável.
🔗 Share this article
✍️ Leave a Comment