Blog

classmethod vs staticmethod em Python

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 cls para 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.

How useful was this post?

Click on a heart to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.