Blog

classmethod vs staticmethod en Python

Tres tipos de método, y elegir entre ellos no es una cuestión de estilo. Es una cuestión de qué necesita el método en realidad.

  • Un método de instancia necesita este objeto en particular.
  • Un classmethod necesita la clase.
  • Un staticmethod no necesita ninguna de las dos.

Los tres en una sola clase

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)))

Imprime:

Ada 52500.0
True False

from_csv es el classmethod clásico: un constructor alternativo. Python tiene un solo __init__ por clase, así que cualquier otra forma de construir el objeto termina siendo un classmethod.

Por qué from_csv usa cls

Esta es la parte que importa, y la razón por la que un classmethod no es simplemente un staticmethod al que le pasan la clase.

# 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)

Imprime:

Contractor Grace 60000.0
after raise: 66000.0

Contractor.from_csv produjo un Contractor, no un Employee, y el aumento usó 1.10. Nadie escribió un from_csv en Contractor: cls era Contractor porque sobre eso se hizo la llamada.

Fija la clase a mano y eso deja 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__)

Imprime:

Employee

Broken.from_csv devuelve un Employee. Todas las subclases reciben el tipo equivocado en silencio, y nada lanza un error. Los constructores alternativos usan cls, siempre.

Qué recibe cada uno

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())

Imprime:

got Show instance
got the class Show
got nothing

Cuándo usar un staticmethod

Pocas veces, y la respuesta honesta es: cuando una función normal serviría pero quieres tenerla bajo el nombre de la clase con la que se relaciona. is_payday no toca ni al empleado ni a la clase: es una función sobre fechas.

Si un staticmethod tampoco tiene nada que ver con la clase, lo que quiere ser es una función a nivel de módulo. Meterla en una clase no te da nada más que un nombre más largo.

Qué recordar

  • Método de instancia para todo lo que lea o cambie este objeto.

  • Classmethod para los constructores alternativos, y usa cls para que las subclases funcionen.

  • Staticmethod cuando el método no necesita ninguna de las dos, y solo cuando agruparlo con la clase de verdad ayude.

  • Fijar el nombre de la clase a mano dentro de un constructor alternativo es un error que aparece recién cuando alguien te hereda.

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.