Programacao & 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.

Equipe Blueprintblog6 min
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 = 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.

python
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:

python
# 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:

DecoratorO que faz
@propertyMétodo que parece atributo
@classmethodMétodo da classe, não do objeto
@staticmethodFunçã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 = 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() 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 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.

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ú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.

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

Tags do artigo

Artigos relacionados

Receba os ultimos artigos no seu email.

Follow Us: