Programação & Dev

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.

Decorators em Python: Entenda @property, @classmethod e @staticmethod

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.

PYTHON
def saudacao():    print("Olá, mundo!")# Guardando a função numa variável (sem os parênteses!)minha_fn = saudacaominha_fn()  # Olá, mundo!# Passando função como argumentodef 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.

PYTHON
import timedef 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 feioprocessar_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:

PYTHON
# Com @ — a forma que você vai usar sempre@medir_tempodef processar_dados():    time.sleep(0.5)    print("Dados processados!")processar_dados()# Dados processados!# ⏱️ processar_dados levou 0.5003s# Funciona em qualquer função!@medir_tempodef 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.

PYTHON
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 = valorcamiseta = Produto("Camiseta Blueprint", 89.90)print(camiseta.preco)    # R$ 89.90  — parece atributo, mas é método!camiseta.preco = 79.90   # usa o setter automaticamenteprint(camiseta.preco)    # R$ 79.90camiseta.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() e set_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.

PYTHON
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 normala1 = 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ápidoa3 = Artigo.rascunho("Ideia de artigo novo")print(a2)  # [Python] Decorators — Genildoprint(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.

PYTHON
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úmeroprint(ValidadorSenha.validar("blueprint1")) # True ✅

⚠️ Regra rápida pra não confundir:

  • Precisa acessar self (dados do objeto)? → método normal

  • Precisa acessar cls (a classe em si)? → @classmethod

  • Não precisa de nenhum dos dois? → @staticmethod


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.

PYTHON
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 wrapperclass 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.

  • @property cria acesso controlado a atributos privados.

  • @classmethod opera 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.