Métodos de instância recebem self, métodos de classe recebem cls e métodos estáticos não recebem nenhum dos dois automaticamente. A escolha deve refletir a dependência real do comportamento.
Um construtor alternativo
from dataclasses import dataclass
from datetime import date
@dataclass(frozen=True)
class Assinatura:
inicio: date
@classmethod
def a_partir_de_iso(cls, valor: str) -> "Assinatura":
return cls(inicio=date.fromisoformat(valor))
@staticmethod
def formato_aceito() -> str:
return "AAAA-MM-DD"
Usar cls(...) preserva subclasses, ao contrário de escrever Assinatura(...) dentro do método. staticmethod faz sentido quando a operação pertence conceitualmente à API da classe, mas não precisa de estado.
Critério de escolha
Use método de instância para comportamento de um objeto. Use classmethod para fábricas que precisam construir a classe concreta ou ler configuração de classe. Considere uma função de módulo antes de staticmethod, especialmente quando a lógica serve a vários tipos.
Não use esses decoradores apenas para esconder dependências globais. Passe colaboradores explicitamente e mantenha parsing complexo fora do modelo quando ele tiver outra responsabilidade. Veja dataclasses em Python e programação orientada a objetos.
Como o binding funciona
Funções definidas no corpo de uma classe implementam o protocolo descritor. Ao acessar um método de instância por um objeto, Python cria um método vinculado e fornece o objeto como primeiro argumento. Ao acessá-lo pela classe, você precisa fornecer a instância:
class Contador:
def incrementar(self, valor: int) -> int:
return valor + 1
contador = Contador()
assert contador.incrementar(2) == 3
assert Contador.incrementar(contador, 2) == 3
classmethod muda esse binding para enviar a classe que foi usada no acesso. staticmethod desativa o binding automático e devolve a função sem acrescentar argumento. Os nomes self e cls são convenções, não palavras reservadas, mas respeitá-los torna a intenção imediatamente reconhecível.
classmethod e herança
O principal benefício de cls aparece quando há subclasses. Uma fábrica deve construir o tipo pelo qual foi chamada:
@dataclass(frozen=True)
class AssinaturaAnual(Assinatura):
desconto: int = 10
plano = AssinaturaAnual.a_partir_de_iso("2026-09-01")
assert isinstance(plano, AssinaturaAnual)
Isso funciona porque o método chama cls(...). Se chamasse Assinatura(...), sempre devolveria a base. A fábrica precisa, porém, respeitar o construtor das subclasses. Acrescentar campos obrigatórios incompatíveis pode quebrar a herança. Nesse cenário, sobrescreva a fábrica, dê padrões adequados ou prefira um serviço externo.
Métodos de classe também podem consultar atributos da classe concreta:
class Importador:
separador = ","
@classmethod
def separar(cls, linha: str) -> list[str]:
return linha.split(cls.separador)
class ImportadorTsv(Importador):
separador = "\t"
ImportadorTsv.separar(...) usa tabulação sem duplicar a lógica. Isso é útil para configuração estável por subtipo. Configuração que muda por ambiente ou requisição deve ser injetada, não guardada em estado global de classe.
Construtores alternativos bem desenhados
Uma fábrica com nome descritivo mostra qual representação aceita: de_json, de_linha_csv ou a_partir_de_configuracao. Ela deve validar sua entrada e devolver uma instância válida. Se houver I/O, cache, rede ou muitas dependências, uma função ou classe de serviço separada costuma ser melhor.
Anote o retorno com o próprio tipo. Em Python moderno, typing.Self expressa que o retorno acompanha a classe concreta:
from typing import Self
class Usuario:
def __init__(self, nome: str) -> None:
self.nome = nome
@classmethod
def de_texto(cls, texto: str) -> Self:
nome = texto.strip()
if not nome:
raise ValueError("nome vazio")
return cls(nome)
Para versões anteriores a Python 3.11, use uma variável de tipo limitada ou uma referência textual, conforme a compatibilidade do projeto. A anotação não substitui a validação.
Quando staticmethod é apropriado
Um método estático pode manter perto do tipo uma operação pequena que pertence claramente ao seu vocabulário, como validar um formato usado apenas por aquela classe. Ele também evita que uma função seja vinculada acidentalmente quando armazenada no corpo da classe.
class CodigoProduto:
@staticmethod
def normalizar(valor: str) -> str:
return valor.strip().upper()
Se normalizar passa a ser usada por clientes, pedidos e importadores, movê-la para um módulo compartilhado deixa a dependência mais explícita. staticmethod não dá acesso especial a atributos privados, não torna a função mais rápida e não cria isolamento de estado.
Sobrescrita e chamadas com super()
Métodos de instância e de classe participam naturalmente do polimorfismo. Um classmethod sobrescrito recebe a subclasse e pode delegar com super(). Métodos estáticos também podem ser sobrescritos, mas a chamada direta pela classe fixa pode ignorar a versão da subclasse. Se a variação polimórfica importa, classmethod ou método de instância geralmente comunica melhor essa intenção.
Evite alternar o tipo de método na sobrescrita, por exemplo transformar um método de instância em estático. Mesmo que certas chamadas pareçam funcionar, o contrato fica surpreendente para leitores, subclasses e ferramentas de tipagem.
Testes e erros comuns
Teste fábricas alternativas com entrada válida, limites e erros de parsing. Inclua uma subclasse quando preservar herança fizer parte do contrato. Para métodos estáticos puros, teste entrada e saída como faria com uma função.
Não use classmethod como substituto de estado global mutável. Alterar atributos de classe em testes pode vazar para outros casos e criar concorrência difícil de diagnosticar. Não crie staticmethod apenas porque o método ainda não usa self; talvez ele represente comportamento do objeto e venha a precisar de estado, ou talvez deva ser função de módulo. Escolha pela responsabilidade pública, não pelo formato momentâneo do corpo.
Inspeção e chamadas equivalentes
Observar os atributos ajuda a consolidar a diferença. Assinatura.a_partir_de_iso já é um método vinculado à classe; Assinatura.formato_aceito é a função estática acessível pelo namespace. No dicionário bruto da classe, antes do protocolo descritor agir, os valores são objetos classmethod e staticmethod.
Você pode chamar um método estático por uma instância, mas isso costuma esconder sua independência. Prefira CodigoProduto.normalizar(valor) para deixar claro que nenhum objeto participa. Para método de classe, chamar pela instância é válido, porém Tipo.fabrica(...) comunica melhor que uma nova instância será criada.
Decoradores precisam estar na ordem correta quando combinados. @classmethod envolve a função e deve aparecer como o decorador externo em usos normais. Evite combinações exóticas com property; APIs explícitas e compatíveis com verificadores são mais fáceis de manter.
Antes de escolher, formule uma pergunta: o resultado depende de qual objeto recebeu a chamada, de qual classe recebeu a chamada ou de nenhum dos dois? A resposta aponta respectivamente para método de instância, classmethod ou função independente. Use staticmethod quando o namespace da classe realmente acrescentar significado.
A documentação oficial de classmethod e staticmethod, consultada em 22 de julho de 2026, descreve binding e herança. Uma API pequena e previsível é mais importante que concentrar toda função relacionada dentro da classe.