Três tipos de método, e escolher entre eles não é questão de estilo. É questão do que o método realmente precisa.
- Um método de instância precisa deste objeto em particular.
- Um classmethod precisa da classe.
- Um staticmethod não precisa de nenhum dos dois.
Os três numa classe só
from datetime import date
class Employee:
raise_pct = 1.05
def __init__(self, name, salary):
self.name, self.salary = name, salary
def give_raise(self): # instance method: needs this employee
self.salary = round(self.salary * self.raise_pct, 2)
@classmethod
def from_csv(cls, line): # classmethod: needs the class
name, salary = line.split(',')
return cls(name, float(salary))
@staticmethod
def is_payday(d): # staticmethod: needs neither
return d.day == 25
e = Employee.from_csv('Ada,50000')
e.give_raise()
print(e.name, e.salary)
print(Employee.is_payday(date(2026, 9, 25)), Employee.is_payday(date(2026, 9, 3)))
Ele imprime:
Ada 52500.0
True False
from_csv é o classmethod clássico: um construtor alternativo. O Python tem um __init__ por classe, então toda outra maneira de construir o objeto vira um classmethod.
Por que from_csv usa cls
Essa é a parte que importa, e o motivo de um classmethod não ser só um staticmethod que por acaso recebe a classe.
# why from_csv uses cls and not Employee
class Contractor(Employee):
raise_pct = 1.10
c = Contractor.from_csv('Grace,60000')
print(type(c).__name__, c.name, c.salary)
c.give_raise()
print('after raise:', c.salary)
Ele imprime:
Contractor Grace 60000.0
after raise: 66000.0
Contractor.from_csv produziu um Contractor, não um Employee, e o aumento usou 1.10. Ninguém escreveu um from_csv no Contractor — o cls era Contractor porque foi nele que a chamada foi feita.
Fixe a classe no código e isso para de funcionar:
# hard-coding the class breaks the subclass
class Broken(Employee):
@classmethod
def from_csv(cls, line):
name, salary = line.split(',')
return Employee(name, float(salary)) # wrong: always the base class
print(type(Broken.from_csv('Alan,70000')).__name__)
Ele imprime:
Employee
Broken.from_csv devolve um Employee. Toda subclasse recebe o tipo errado em silêncio, e nada levanta erro. Construtores alternativos usam cls, sempre.
O que cada um recebe
class Show:
def instance(self): return f"got {type(self).__name__} instance"
@classmethod
def klass(cls): return f"got the class {cls.__name__}"
@staticmethod
def static(): return "got nothing"
s = Show()
print(s.instance())
print(Show.klass())
print(Show.static())
Ele imprime:
got Show instance
got the class Show
got nothing
Quando usar um staticmethod
Raramente, e a resposta honesta é: quando uma função comum resolveria, mas você quer ela no mesmo namespace da classe a que se relaciona. is_payday não toca num funcionário nem na classe — é uma função sobre datas.
Se um staticmethod também não tem nada a ver com a classe, ele quer ser uma função no nível do módulo. Colocar numa classe não te dá nada além de um nome mais comprido.
O que lembrar
-
Método de instância para qualquer coisa que lê ou altera este objeto.
-
Classmethod para construtores alternativos, e use
clspara que subclasses funcionem. -
Staticmethod quando o método não precisa de nenhum dos dois, e só quando agrupar com a classe realmente ajuda.
-
Fixar o nome da classe dentro de um construtor alternativo é um bug que só aparece quando alguém te subclasse.