🌗 ← Índice
Data: 14/08/2026
Nível: Intermediário (Mês 2 — POO)
Um decorator é uma função que recebe outra função e devolve uma função modificada. O @ é açúcar sintático:
def meu_decorator(func):
def wrapper(*args, **kwargs):
print(f"→ Chamando {func.__name__}...")
resultado = func(*args, **kwargs)
print(f"← {func.__name__} retornou {resultado!r}")
return resultado
return wrapper
@meu_decorator
def soma(a, b):
return a + b
soma(2, 3) # → Chamando soma... / ← soma retornou 5
💡 `@meu_decorator` é o mesmo que `soma = meu_decorator(soma)`.
def repetir(vezes):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(vezes):
func(*args, **kwargs)
return wrapper
return decorator
@repetir(3)
def oi():
print("Oi!")
oi() # Oi! Oi! Oi!
from functools import wraps
def log(func):
@wraps(func) # copia __name__, __doc__ etc.
def wrapper(*args, **kwargs):
print(f"Executando {func.__name__}")
return func(*args, **kwargs)
return wrapper
Sem `@wraps`, `soma.__name__` viraria `"wrapper"` — confuso para debugging.
class Calculadora:
taxa = 0.1
def __init__(self, valor):
self.valor = valor
def com_imposto(self): # usa self (instância)
return self.valor * (1 + self.taxa)
@staticmethod
def dobrar(n): # não usa self nem cls
return n * 2
@classmethod
def com_taxa_personalizada(cls, valor, taxa):
nova = cls(valor) # usa cls (classe)
nova.taxa = taxa
return nova
| Tipo | Recebe | Acesso |
|---|---|---|
| método normal | `self` | instância |
| `@classmethod` | `cls` | classe (pode alterar estado da classe) |
| `@staticmethod` | nada | utilitário sem dependência |
class Temperatura:
def __init__(self, celsius):
self._celsius = celsius
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, valor):
if valor < -273.15:
raise ValueError("Temperatura abaixo do zero absoluto!")
self._celsius = valor
t = Temperatura(25)
print(t.fahrenheit) # 77.0
t.celsius = 30 # usa o setter
class ArquivoAberto:
def __init__(self, nome, modo="r"):
self.arquivo = open(nome, modo)
def __enter__(self):
return self.arquivo
def __exit__(self, exc_type, exc_val, exc_tb):
self.arquivo.close()
return False # False = não engole exceções
with ArquivoAberto("dados.txt") as f:
conteudo = f.read()
# arquivo fechado automaticamente, mesmo com erro
from contextlib import contextmanager
@contextmanager
def arquivo_aberto(nome, modo="r"):
arquivo = open(nome, modo)
try:
yield arquivo # entrega o recurso
finally:
arquivo.close() # garante limpeza
with arquivo_aberto("dados.txt") as f:
print(f.read())
Crie um decorator validar_positivo que:
@validar_positivo("preco", "quantidade")
def calcular_total(preco, quantidade, desconto=0):
return preco * quantidade - desconto
Teste: calcular_total(10, -2) deve falhar; calcular_total(10, 2) deve retornar 20.
from functools import wraps
def validar_positivo(*nomes):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# monta dicionário de argumentos
import inspect
ligados = inspect.signature(func).bind(*args, **kwargs)
ligados.apply_defaults()
for nome in nomes:
valor = ligados.arguments.get(nome)
if valor is not None and valor <= 0:
raise ValueError(
f"Argumento '{nome}' deve ser positivo (recebido: {valor})"
)
return func(*args, **kwargs)
return wrapper
return decorator
@validar_positivo("preco", "quantidade")
def calcular_total(preco, quantidade, desconto=0):
return preco * quantidade - desconto
print(calcular_total(10, 2)) # 20
# calcular_total(10, -2) # ValueError: Argumento 'quantidade' deve ser positivo
💡 Dica extra: inspect.signature().bind() é o jeito profissional de ler argumentos nomeados dentro de um wrapper.
HB Estudos · ← Voltar ao índice de Python