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
clspara 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.