Blog

classmethod vs staticmethod en Python

La pregunta siempre es solo qué necesita el método: esta instancia, la clase, o ninguna de las dos. Equivocarse rompe las subclases sin avisar.

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.

¿Qué tan útil te resultó este post?

¡Haz clic en un corazón para calificar!

Calificación promedio 0 / 5. Total de votos: 0

Todavía no hay votos. Sé el primero en calificar este post.