Al trabajar con clases en Python, te encontrarás con el decorador @classmethod. Este tiene un propósito único que se distingue claramente de @staticmethod y los métodos de instancia (Instance Method).
En este artículo, clarificaremos qué es @classmethod, y en qué situaciones y cómo debe ser utilizado en comparación con @staticmethod y los métodos de instancia.
1. Tres tipos de métodos: Instance vs. Class vs. Static
Las clases en Python tienen, por defecto, tres tipos de métodos. La mayor diferencia radica en qué recibe como primer argumento el método.
class MyClass:
# 1. Método de instancia (Instance Method)
def instance_method(self, arg1):
# 'self' recibe la instancia de la clase.
# Puede acceder y modificar atributos de instancia (self.x).
print(f"Método de instancia llamado con {self} y {arg1}")
# 2. Método de clase (Class Method)
@classmethod
def class_method(cls, arg1):
# 'cls' recibe la clase misma (MyClass).
# Se utiliza principalmente para acceder a atributos de clase,
# o actuar como un 'fábrica' que crea instancias.
print(f"Método de clase llamado con {cls} y {arg1}")
# 3. Método estático (Static Method)
@staticmethod
def static_method(arg1):
# No recibe 'self' ni 'cls'.
# Es una función utilitaria que no tiene relación con el estado de la clase o instancia.
# Esta función solo pertenece a la clase.
print(f"Método estático llamado con {arg1}")
# Ejemplo de llamada de método
obj = MyClass()
# Método de instancia (llamado a través de la instancia)
obj.instance_method("Hola")
# Método de clase (puede ser llamado a través de la clase o instancia)
MyClass.class_method("Mundo")
obj.class_method("Mundo") # Esto también es posible, pero es más claro llamarlo a través de la clase.
# Método estático (puede ser llamado a través de la clase o instancia)
MyClass.static_method("Python")
obj.static_method("Python")
Comparación a simple vista
| Clasificación | Primer argumento | Acceso | Uso principal |
|---|---|---|---|
| Método de instancia | self (instancia) |
Atributos de instancia (self.name) |
Manipular o acceder al estado del objeto |
| Método de clase | cls (clase) |
Atributos de clase (cls.count) |
Constructor alternativo (fábrica) |
| Método estático | Ninguno | Ninguno | Función utilitaria lógicamente relacionada con la clase |
2. Uso clave de @classmethod: Método de fábrica
El caso de uso más importante y común de @classmethod es definir un "Constructor alternativo" o "Método de fábrica".
El constructor por defecto, __init__, generalmente recibe argumentos claros para inicializar un objeto. Sin embargo, a veces es necesario crear un objeto de otra manera (por ejemplo, analizando cadenas, diccionarios, valores calculados específicos).
Usando @classmethod, puedes crear otro 'punto de entrada' que devuelva una instancia de la clase además de __init__.

Ejemplo: Creando un objeto Person a partir de una cadena de fecha
Supongamos que la clase Person recibe name y age. ¿Cómo crear un objeto a partir de una cadena en formato 'nombre-año de nacimiento'?
import datetime
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"Hola, soy {self.name} y tengo {self.age} años.")
@classmethod
def from_birth_year(cls, name, birth_year):
"""
Recibe el año de nacimiento y calcula la edad para crear una instancia de Person.
"""
current_year = datetime.datetime.now().year
age = current_year - birth_year
# 'cls' se refiere a la clase Person.
# Es equivalente a return Person(name, age), pero,
# al usar 'cls', se beneficia en caso de herencia. (se explicará a continuación)
return cls(name, age)
# Usando el constructor por defecto
person1 = Person("Alice", 30)
person1.greet()
# Usando el constructor alternativo creado con @classmethod
person2 = Person.from_birth_year("MadHatter", 1995)
person2.greet()
Resultado:
Hola, soy Alice y tengo 30 años.
Hola, soy MadHatter y tengo 30 años. (basado en el año actual 2025)
En el ejemplo anterior, el método from_birth_year accede a la propia clase Person a través del argumento cls. Luego usa la age calculada para llamar a cls(name, age), lo que es equivalente a llamar a Person(name, age) y devuelve una nueva instancia de Person.
3. @classmethod y herencia
"¿Por qué no puedo simplemente llamar a Person(...)? ¿Por qué usar cls(...)?"
El verdadero poder de @classmethod se revela en la herencia. cls apunta a _la clase exacta_ en la que se llamó al método.
Supongamos que creamos una clase Student que hereda de Person.
class Student(Person):
def __init__(self, name, age, major):
super().__init__(name, age) # Llama a __init__ de la clase padre
self.major = major
def greet(self):
# Sobrescritura del método
print(f"Hola, soy {self.name}, tengo {self.age} años y estudio {self.major}.")
# Llamando al @classmethod del padre desde la clase Student.
student1 = Student.from_birth_year("Jhon", 2005, "Ingeniería de Computadores")
# ¡Ups, ocurre un TypeError!
# Al llamar a Student.from_birth_year,
# se ejecuta Person.from_birth_year,
# donde 'cls' es 'Student'.
# Es decir, return cls(name, age) se convierte en return Student(name, age).
# Sin embargo, __init__ de Student requiere el argumento 'major' adicional.
El código anterior falla porque __init__ de Student necesita major. Pero la clave es que cls apunta a Student y no a Person.
Si from_birth_year hubiera sido @staticmethod, o si se hubiera codificado Person(name, age) directamente, entonces al llamar a Student.from_birth_year, se habría devuelto un objeto Person en lugar de uno de Student.
El uso de @classmethod garantiza que, a través de cls, se crea correctamente una instancia de la clase hija, incluso si es llamada desde la clase hija. (Cabe aclarar que si la firma de __init__ de la clase hija cambia, from_birth_year también tiene que ser sobreescrito.)
4. Resumen
En resumen, aquí están los puntos clave sobre @classmethod.
-
@classmethodtomacls(la clase misma) como primer argumento. -
Su uso principal es como 'constructor alternativo (método de fábrica)'. Se usa al ofrecer diferentes maneras de crear un objeto además de
__init__. -
Es poderoso cuando se usa con herencia. Al llamarlo desde una clase hija,
clsapunta a la clase hija correcta, creando su instancia.
Recuerda que @staticmethod es para funciones utilitarias que no requieren el estado de la clase o instancia en absoluto, @classmethod es para métodos de fábrica que deben acceder a la propia clase, y los métodos de instancia se utilizan para trabajar con el estado del objeto (self), lo que hará que tu código sea mucho más claro y 'pythónico'.
No hay comentarios.