Este contenido solo está disponible en Portugués.
Aún sin traducción para este idioma.
Decorators em Python: Entenda @property, @classmethod e @staticmethod
Você já se perguntou o que são @property, @classmethod e @staticmethod em Python? Este guia completo desmistifica os decorators, explicando desde o conceito fundamental de funções como cidadãs de primeira classe até a criação de decorators customizados. Entenda a sintaxe '@', veja exemplos práticos e aprenda a usar os decorators de OOP para escrever código elegante e reutilizável.

Série OOP em Python — Parte 2
Parte 1 — OOP em Python: do zero, de verdade
Você já viu @property, @staticmethod e ficou com aquela cara de interrogação? Antes de entender esses três, a gente precisa entender o que um decorator é de verdade.
Primeiro: funções são cidadãs de primeira classe
Em Python, funções são objetos como qualquer outro. Isso significa que você pode passar funções como argumento, retornar funções e guardar funções em variáveis.
def saudacao():
print("Olá, mundo!")
# Guardando a função numa variável (sem os parênteses!)
minha_fn = saudacao
minha_fn() # Olá, mundo!
# Passando função como argumento
def executar(fn):
print("Antes...")
fn()
print("Depois.")
executar(saudacao)
# Antes...
# Olá, mundo!
# Depois.
💡 Por que isso importa? Um decorator nada mais é do que uma função que recebe outra função, adiciona algum comportamento, e retorna uma nova função. É exatamente isso.
Construindo um decorator do zero
Vamos criar um decorator que mede o tempo de execução de qualquer função. Útil pra debug e otimização.
import time
def medir_tempo(funcao):
# funcao é a função que vamos "decorar"
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = funcao(*args, **kwargs) # executa a função original
fim = time.time()
print(f"⏱️ {funcao.__name__} levou {fim - inicio:.4f}s")
return resultado
return wrapper # retorna a função "embrulhada"
# Sem a sintaxe @, seria assim:
def processar_dados():
time.sleep(0.5)
print("Dados processados!")
processar_dados = medir_tempo(processar_dados) # manual e feio
processar_dados()
🧠 Modelo mental: o decorator é um embrulho. A função original entra, o decorator coloca ela dentro de uma caixa com comportamentos extras, e devolve a caixa.
O açúcar sintático: o @
O @ é só uma forma mais bonita de escrever exatamente o que fizemos acima. As duas versões são idênticas:
# Com @ — a forma que você vai usar sempre
@medir_tempo
def processar_dados():
time.sleep(0.5)
print("Dados processados!")
processar_dados()
# Dados processados!
# ⏱️ processar_dados levou 0.5003s
# Funciona em qualquer função!
@medir_tempo
def buscar_usuario(id):
time.sleep(0.1)
return {"id": id, "nome": "Genildo"}
usuario = buscar_usuario(42)
# ⏱️ buscar_usuario levou 0.1001s
✨ Sacou a mágica? Você escreveu a lógica de medir tempo uma vez só e aplicou em qualquer função com um
@. Isso é o princípio DRY (Don't Repeat Yourself) em ação.
Agora sim: decorators de OOP
Python tem três decorators nativos pra classes que você vai ver em todo lugar:
| Decorator | O que faz |
|---|---|
@property | Método que parece atributo |
@classmethod | Método da classe, não do objeto |
@staticmethod | Função independente dentro da classe |
@property — acesso controlado com elegância
Lembra do encapsulamento com __saldo na Parte 1? O @property permite que você acesse dados protegidos como se fossem atributos simples, mas com lógica por baixo.
class Produto:
def __init__(self, nome, preco):
self.nome = nome
self.__preco = preco # privado
@property
def preco(self):
# getter: chamado ao ler produto.preco
return f"R$ {self.__preco:.2f}"
@preco.setter
def preco(self, valor):
# setter: chamado ao atribuir produto.preco = X
if valor < 0:
raise ValueError("Preço não pode ser negativo! 🚫")
self.__preco = valor
camiseta = Produto("Camiseta Blueprint", 89.90)
print(camiseta.preco) # R$ 89.90 — parece atributo, mas é método!
camiseta.preco = 79.90 # usa o setter automaticamente
print(camiseta.preco) # R$ 79.90
camiseta.preco = -10 # ValueError: Preço não pode ser negativo! 🚫
🔶 Quando usar @property? Quando você quer que o acesso a um dado tenha lógica embutida — formatação, validação, cálculo derivado — mas a API externa continue limpa, sem
get_preco()eset_preco().
@classmethod — construindo objetos de formas alternativas
Um @classmethod recebe a classe como primeiro argumento (convencionalmente cls), não o objeto. É muito usado pra criar factory methods — formas alternativas de instanciar uma classe.
class Artigo:
def __init__(self, titulo, autor, categoria):
self.titulo = titulo
self.autor = autor
self.categoria = categoria
@classmethod
def de_dicionario(cls, dados: dict):
# cria um Artigo a partir de um dict (ex: resposta de API)
return cls(
titulo=dados["title"],
autor=dados["author"],
categoria=dados["category"]
)
@classmethod
def rascunho(cls, titulo):
# cria um artigo rascunho com defaults
return cls(titulo=titulo, autor="Desconhecido", categoria="Rascunho")
def __repr__(self):
return f"[{self.categoria}] {self.titulo} — {self.autor}"
# Forma normal
a1 = Artigo("OOP em Python", "Genildo", "Python")
# Via dicionário (vindo de uma API, por exemplo)
payload = {"title": "Decorators", "author": "Genildo", "category": "Python"}
a2 = Artigo.de_dicionario(payload)
# Rascunho rápido
a3 = Artigo.rascunho("Ideia de artigo novo")
print(a2) # [Python] Decorators — Genildo
print(a3) # [Rascunho] Ideia de artigo novo — Desconhecido
@staticmethod — utilidade sem estado
Um @staticmethod não recebe self nem cls. É uma função comum que mora dentro da classe por organização lógica, não por acoplamento.
class ValidadorSenha:
@staticmethod
def tem_tamanho_minimo(senha: str) -> bool:
return len(senha) >= 8
@staticmethod
def tem_numero(senha: str) -> bool:
return any(c.isdigit() for c in senha)
@staticmethod
def validar(senha: str) -> bool:
return (
ValidadorSenha.tem_tamanho_minimo(senha) and
ValidadorSenha.tem_numero(senha)
)
print(ValidadorSenha.validar("abc")) # False — curta e sem número
print(ValidadorSenha.validar("blueprint1")) # True ✅
⚠️ Regra rápida pra não confundir:
Decorator customizado + OOP juntos
Pra fechar com chave de ouro: um decorator que loga automaticamente toda chamada de método numa classe. Esse padrão aparece em frameworks de verdade.
def logar_chamada(funcao):
def wrapper(*args, **kwargs):
print(f"📋 Chamando: {funcao.__name__}()")
resultado = funcao(*args, **kwargs)
print(f"✅ Concluído: {funcao.__name__}()")
return resultado
return wrapper
class ServicoDePost:
def __init__(self):
self.__posts = []
@logar_chamada
def criar_post(self, titulo):
self.__posts.append(titulo)
return titulo
@logar_chamada
def publicar_todos(self):
for post in self.__posts:
print(f" 🚀 Publicando: {post}")
@property
def total(self):
return len(self.__posts)
@staticmethod
def slug(titulo: str) -> str:
return titulo.lower().replace(" ", "-")
servico = ServicoDePost()
servico.criar_post("OOP em Python")
servico.criar_post("Decorators em Python")
servico.publicar_todos()
print(f"Total: {servico.total}")
print(ServicoDePost.slug("Decorators em Python"))
# decorators-em-python
O que você aprendeu hoje
- ✅ Funções são objetos em Python — podem ser passadas e retornadas.
- ✅ Um decorator é uma função que embrulha outra função.
- ✅ O
@é açúcar sintático — não tem mágica, só elegância. - ✅
@propertycria acesso controlado a atributos privados. - ✅
@classmethodopera na classe, não no objeto — ótimo pra factory methods. - ✅
@staticmethodé uma função utilitária que mora na classe por organização. - ✅ Decorators customizados + OOP = código limpo e reutilizável.



