@abstractmethod-1
@classmethod en Python
Un classmethod es un método que pertenece a la clase, no a las instancias. Recibe la clase como primer parámetro (por convención llamado cls) en lugar de la instancia (self).
Diferencias clave:
Método normal: Recibe
self(la instancia)Classmethod: Recibe
cls(la clase)Puede ser llamado desde la clase sin crear una instancia
Ejemplo muy sencillo:
class Persona:
especie = "Humano" # Atributo de clase
def __init__(self, nombre, edad):
self.nombre = nombre # Atributo de instancia
self.edad = edad
# Método normal - necesita una instancia
def presentarse(self):
return f"Hola, soy {self.nombre} y tengo {self.edad} años"
# Classmethod - puede usarse sin instancia
@classmethod
def que_especie(cls):
return f"Somos de la especie: {cls.especie}"
# Classmethod para crear objetos de forma alternativa
@classmethod
def desde_nacimiento(cls, nombre, año_nacimiento):
edad = 2024 - año_nacimiento
return cls(nombre, edad) # Crea una nueva instancia
# Uso
print(Persona.que_especie()) # ¡Sin crear objeto!
# Output: Somos de la especie: Humano
# Método normal necesita instancia
persona1 = Persona("Ana", 25)
print(persona1.presentarse()) # Hola, soy Ana y tengo 25 años
# Usando classmethod como constructor alternativo
persona2 = Persona.desde_nacimiento("Carlos", 1995)
print(persona2.presentarse()) # Hola, soy Carlos y tengo 29 años
# También se puede llamar desde una instancia
print(persona1.que_especie()) # Somos de la especie: HumanoCasos de uso comunes:
1. Constructores alternativos
class Fecha:
def __init__(self, dia, mes, año):
self.dia = dia
self.mes = mes
self.año = año
@classmethod
def desde_string(cls, fecha_str):
"""Crea Fecha desde string 'dd-mm-aaaa'"""
dia, mes, año = map(int, fecha_str.split('-'))
return cls(dia, mes, año)
@classmethod
def hoy(cls):
"""Crea Fecha con la fecha de hoy"""
from datetime import date
hoy = date.today()
return cls(hoy.day, hoy.month, hoy.year)
# Uso
fecha1 = Fecha(15, 3, 2024) # Constructor normal
fecha2 = Fecha.desde_string("20-05-2024") # Constructor alternativo
fecha3 = Fecha.hoy() # Otro constructor alternativo2. Modificar atributos de clase
class Contador:
contador_global = 0
def __init__(self):
self.id = Contador.contador_global
Contador.incrementar_contador()
@classmethod
def incrementar_contador(cls):
cls.contador_global += 1
@classmethod
def reiniciar_contador(cls):
cls.contador_global = 0
# Uso
obj1 = Contador()
obj2 = Contador()
print(Contador.contador_global) # 2
Contador.reiniciar_contador()
obj3 = Contador()
print(Contador.contador_global) # 1En resumen:
@classmethod trabaja con la clase (no con instancias)
Recibe
clscomo primer parámetroNo necesita crear un objeto para usarlo
Útil para: constructores alternativos, manipular atributos de clase, métodos que no dependen de instancias específicas
¿Te queda claro? ¡Es mucho más simple de lo que parece!
@classmethod en Clase Suma: El Contador Global 🧮
Excelente pregunta! Vamos con tu ejemplo de la clase Suma para entenderlo claramente.
Primero, entendamos el problema
class Suma:
def __init__(self, a, b):
self.a = a
self.b = b
def calcular(self):
return self.a + self.b
# Diferentes instancias, diferentes resultados
suma1 = Suma(2, 3)
suma2 = Suma(5, 7)
suma3 = Suma(10, 15)
print(suma1.calcular()) # 5
print(suma2.calcular()) # 12
print(suma3.calcular()) # 25Hasta aquí: Cada instancia es independiente.
Ahora agreguemos @classmethod
class Suma:
# Atributo de CLASE (compartido por todos)
total_sumas = 0
historial = []
def __init__(self, a, b):
self.a = a
self.b = b
# Contamos CADA nueva suma creada
Suma.total_sumas += 1
def calcular(self):
resultado = self.a + self.b
# Guardamos en el historial
Suma.agregar_al_historial(f"{self.a} + {self.b} = {resultado}")
return resultado
# @classmethod para trabajar con datos de TODAS las sumas
@classmethod
def obtener_estadisticas(cls):
return f"Total de sumas realizadas: {cls.total_sumas}"
@classmethod
def obtener_historial(cls):
return cls.historial
@classmethod
def agregar_al_historial(cls, operacion):
cls.historial.append(operacion)
@classmethod
def reiniciar_contador(cls):
cls.total_sumas = 0
cls.historial = []
# @classmethod como método de fábrica
@classmethod
def desde_lista(cls, numeros):
if len(numeros) >= 2:
return cls(numeros[0], numeros[1])
else:
return cls(0, 0)
@classmethod
def desde_cadena(cls, cadena):
# Convierte "5+3" en Suma(5, 3)
try:
a, b = map(int, cadena.split('+'))
return cls(a, b)
except:
return cls(0, 0)¿Cómo funciona esto en la práctica?
# Creamos sumas de diferentes maneras
suma1 = Suma(2, 3)
suma2 = Suma(5, 7)
suma3 = Suma.desde_lista([10, 15]) # Usando classmethod
suma4 = Suma.desde_cadena("8+4") # Usando classmethod
# Cada instancia calcula su propia suma
print(suma1.calcular()) # 5
print(suma2.calcular()) # 12
print(suma3.calcular()) # 25
print(suma4.calcular()) # 12
# Pero los @classmethod ven el panorama completo
print(Suma.obtener_estadisticas()) # Total de sumas realizadas: 4
print(Suma.obtener_historial())
# ['2 + 3 = 5', '5 + 7 = 12', '10 + 15 = 25', '8 + 4 = 12']Analogía: La Oficina de Contabilidad 📊
OFICINA CONTABLE (Clase Suma)
TRABAJADORES INDIVIDUALES (instancias)
├── suma1: 2 + 3 = 5
├── suma2: 5 + 7 = 12
└── suma3: 10 + 15 = 25
GERENTE (@classmethod) - Ve el panorama completo
├── total_sumas = 3
├── historial = ["2+3=5", "5+7=12", "10+15=25"]
└── Puede reiniciar todo o crear nuevos trabajadoresPuntos clave entendidos:
✅ SÍ puedes afectar a todas las instancias:
# Esto afecta a TODAS las sumas existentes y futuras
Suma.reiniciar_contador()
# Ahora todas las instancias verán los cambios
print(Suma.total_sumas) # 0
print(suma1.total_sumas) # 0 (¡sí, desde la instancia también!)✅ SÍ puedes crear métodos que no necesitan instancias:
# Sin crear ninguna suma, puedo usar classmethods
print(Suma.obtener_estadisticas()) # Funciona sin instancias!
# Puedo crear instancias de formas alternativas
suma_nueva = Suma.desde_cadena("20+30") # Sin usar __init__ directamente❌ NO puedes acceder a datos específicos de instancias:
class Suma:
@classmethod
def metodo_mal_uso(cls):
# ESTO NO FUNCIONA - classmethod no sabe sobre instancias específicas
# print(self.a) # ❌ Error - no hay self!
# print(cls.a) # ❌ Error - cls es la clase, no una instancia
passResumen final:
@classmethod te permite:
Trabajar con datos GLOBALES de la clase (compartidos por todos)
Crear "métodos de fábrica" para instanciar de formas alternativas
Acceder a funcionalidad sin necesidad de crear instancias
Modificar comportamiento que afecta a TODAS las instancias
Pero NO puede:
Acceder a datos específicos de una instancia individual
Reemplazar métodos que necesitan trabajar con datos de instancia
¡Es como tener un gerente que ve todas las operaciones pero no hace las sumas individuales!
Comentarios
Publicar un comentario