- ¿Qué es un método estático?
- Atributos de clase
- Métodos de clase y acceso a atributos heredados
- Caso práctico con herencia
En esta lección vamos a de los métodos estáticos (@staticmethod), los métodos de clase (@classmethod) y los atributos de clase.
Aunque estos conceptos pueden parecer similares, en realidad cumplen roles diferentes. Juntos, vamos a entender cómo funcionan, por qué son útiles y cómo usarlos correctamente, sobre todo cuando trabajamos con herencia entre clases.
¿Qué es un método estático?
@staticmethod.
class A:
@staticmethod
def saludo():
print("Hola desde A")
A.saludo() # 'Hola desde A'
Como puedes ver, no necesitamos crear un objeto de A. Llamamos el método directamente desde el nombre de la clase.
Ahora, veamos un ejemplo un poco más interesante con herencia:
class A:
@staticmethod
def quien():
print('A')
@staticmethod
def prueba():
A.quien()
class B(A):
@staticmethod
def quien():
print('B')
B.prueba() # 'A'
Aunque B hereda de A y redefine el método quien(), el método prueba() llama a A.quien(), no a B.quien(). Esto pasa porque al ser un método estático, no tiene conocimiento del contexto del objeto o clase desde donde se está llamando. Se ejecuta exactamente tal como está escrito, lo cual puede ser una limitación con la herencia.
Atributos de clase
Un caso práctico muy común: imaginemos que estamos trabajando con una base de datos. Cada modelo representa una tabla.
class Usuario:
_tabla = 'usuarios'
Este atributo _tabla es importante para saber en qué tabla se debe guardar un objeto Usuario. Como esta información es compartida por todos los usuarios, no tiene sentido guardarla en cada objeto. Por eso, lo hacemos como atributo de clase.
Ahora, queremos crear una clase base que pueda manejar la lógica común para varios modelos:
class EntidadBase:
pass
class Usuario(EntidadBase):
_tabla = 'usuarios'
❓ ¿Y si ahora queremos acceder al nombre de tabla desde el método de EntidadBase?
Vamos a intentarlo.
class EntidadBase:
def obtener_tabla():
return _tabla # Esto da error
print(Usuario.obtener_tabla()) # NameError
Esto no funciona porque _tabla no está definido dentro de EntidadBase. Pero sí está en Usuario, y podemos resolver esto fácilmente usando un método de clase.
Métodos de clase y acceso a atributos heredados
Un método de clase se define usando el decorador @classmethod. A diferencia del método estático, el método de clase recibe como primer argumento a la clase en sí (con la palabra cls, por convención).
Así podemos acceder no solo a atributos definidos en la clase base, sino también a los que definen las subclases.
class EntidadBase:
@classmethod
def obtener_tabla(cls):
return cls._tabla
class Usuario(EntidadBase):
_tabla = 'usuarios'
print(Usuario.obtener_tabla()) # 'usuarios'
Fíjate que el método obtener_tabla se define en EntidadBase, pero cuando lo llamamos desde Usuario, accede al atributo _tabla definido en Usuario. Eso es porque cls representa la clase desde donde se llama el método, no necesariamente donde se definió.
Este comportamiento es muy útil cuando estamos diseñando jerarquías de clases en donde cada subclase puede tener sus propios valores.
Comparación rápida
| Elemento | Decorador | Primer argumento | ¿Accede a instancia? | ¿Accede a clase? | Útil para... |
|---|---|---|---|---|---|
| Método normal | (ninguno) | self |
Sí | No | Acciones que dependen de datos del objeto |
| Método de clase | @classmethod |
cls |
No | Sí | Lógica relacionada a la clase o herencia |
| Método estático | @staticmethod |
ninguno | No | No | Funciones utilitarias dentro de la clase |
Caso práctico con herencia
Imaginemos que tenemos un sistema de almacenamiento donde varias entidades se guardan en tablas distintas. Podemos crear una clase base que contenga la lógica común de guardado y que acceda a la tabla correcta según la clase que se esté usando.
class EntidadBase:
@classmethod
def guardar(cls, datos):
print(f"Guardando en tabla '{cls._tabla}': {datos}")
class Usuario(EntidadBase):
_tabla = 'usuarios'
class Producto(EntidadBase):
_tabla = 'productos'
Usuario.guardar({'nombre': 'Laura'}) # Guardando en tabla 'usuarios': {'nombre': 'Laura'}
Producto.guardar({'nombre': 'Sombrero'}) # Guardando en tabla 'productos': {'nombre': 'Sombrero'}
Gracias a @classmethod y el uso de cls._tabla, podemos reutilizar guardar() sin duplicar el código en cada clase hija. Y cada clase puede tener su propia tabla.
Resumen
- Los métodos estáticos (
@staticmethod) no conocen ni el objeto (self) ni la clase (cls). Son como funciones normales pero agrupadas dentro de una clase. - Los métodos de clase (
@classmethod) reciben la clase como primer argumento (cls) y permiten acceder a atributos del propio tipo, incluso si fueron definidos en una subclase. - Los atributos de clase son valores compartidos por todos los objetos de esa clase. No dependen del estado de un objeto específico.
- Para trabajar bien con herencia y lógica común entre entidades, es mejor usar métodos de clase en lugar de métodos estáticos.
- Usar
clsen lugar de referirnos directamente al nombre del padre (A,EntidadBase, etc.) nos permite respetar la jerarquía de herencia y mantener flexibilidad.
Para acceder completo a curso necesitas un plan básico
El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.