POO: Métodos de clase y estáticos en Python

Publicado por Andrea Navarro el

En este artículo veremos la diferencia entre métodos estáticos y métodos de clase, cómo implementarlos correctamente, y sus usos más comunes.

Métodos estáticos

Los métodos tradicionales, también llamados métodos de instancia, requieren de una instancia de un objeto para ser llamados. El primer parámetro de estos métodos es siempre self, esto le permite acceder completamente al objeto, pudiendo leer y modificar los atributos del mismo.

Los métodos estáticos, por otro lado, pueden ser llamados tanto desde una instancia como desde la clase misma. Al no recibir self no pueden acceder a atributos de clase o de instancia, y se comportan como funciones normales.

Se utilizan principalmente para agrupar funciones relacionadas lógicamente dentro de una misma clase, y que no dependen de un estado específico como validaciones, conversiones y cálculos auxiliares.

Para definir un método como estático se utiliza el decorador @staticmethod.

class Temperatura:
    def __init__(self, min, max):
            self.min = min
            self.max = max

    def obtener_promedio(self):
        return (self.max+self.min)/2
    
    @staticmethod
    def fahrenheit_a_celsius(fahrenheit):
        return (fahrenheit - 32) * 5/9  

En este ejemplo, la clase Temperatura cuenta con métodos de instancia y uno de clase.

El método obtener_promedio es un método de instancia, utiliza self para acceder a los atributos min y max del objeto para obtener el promedio de temperatura.

El método fahrenheit_a_celsius es un método estático definido de esta manera por su decorador. Solo utiliza parámetros de fuera de la clase y no hace uso de ninguno de sus atributos. Se utiliza cuando se quiere hacer una conversión de temperaturas aunque estas puedan no estar relacionadas con una instancia.

temp_hoy = Temperatura(12,24)

print (temp_hoy.obtener_promedio())
print(temp_hoy.fahrenheit_a_celsius(70))

print(Temperatura.fahrenheit_a_celsius(80))

Para llamar al método de instancia obtener_promedio() se debe primero crear una instancia de la clase Temperatura. El método fahrenheit_a_celsius sin embargo puede ser llamado tanto por una instancia de la clase Temperatura como por la clase Temperatura, como vimos en el ejemplo anterior.

Métodos de clase

Los métodos de clase son similares a los métodos estáticos. Ambos pueden accederse desde la clase y no acceden a los atributos de instancia.

Existe, sin embargo, una diferencia importante: los métodos de clase pueden acceder y modificar los atributos de clase.

Para crear un método de clase se usa el decorador @classmethod, y se utiliza como primer parámetro cls en lugar de self, haciendo referencia a la clase, y permitiendo acceder a sus atributos.

Estos métodos se utilizan cuando se necesitan métodos que trabajen con atributos de clase, y son una parte fundamental de muchos patrones de diseño como Factory y Singleton, para la creación de constructores alternativos y realizar polimorfismo a nivel de clase.

class Usuario:    
    total_usuarios = 0
    
    def __init__(self, nombre):
        self.nombre = nombre
        self.puntaje = 0     
        Usuario.total_usuarios += 1
    
    @classmethod
    def mostrar_total(cls):        
        print(f"Total de usuarios {cls.total_usuarios}")

    def sumar_puntaje(self, puntaje):
         self.puntaje += puntaje  

En este ejemplo sencillo existe una clase Usuario que almacena el nombre de cada usuario y su puntaje (que comienza en 0).

Como se quiere saber en todo momento cuantos usuarios se han creado, se especifica el atributo de clase total_usuarios, que es incrementado en __init__ cada vez que se crea un usuario nuevo.

Para ver este valor se crea el método de clase mostrar_total, que tiene como parámetro cls y accede al atributo de clase.

usuario1 = Usuario("Ana")
usuario2 = Usuario("Carlos")

Usuario.mostrar_total() 

usuario3 = Usuario("Beatriz")
usuario3.sumar_puntaje(150)
usuario3.mostrar_total()

El método de clase mostrar_total puede ser accedido tanto desde la clase Usuario como de una de sus instancias.

Constructor alternativo con método de clase

Los métodos de clase pueden ser utilizados para crear constructores alternativos. Estos pueden ser utilizados para separar la lógica de diferentes modos de creación de instancias.

Las instancias se crean dentro del método de clase utilizando el método cls, que ejecutará el __init__ de la clase.

class CuentaBancaria:
    def __init__(self, titular, saldo, tipo="corriente"):       
        self.titular = titular
        self.saldo = saldo
        self.tipo = tipo
    
    @classmethod
    def cuenta_desde_transferencia(cls, titular, monto, origen):
        saldo_inicial = monto
        tipo = f"recibida desde {origen}"
        return cls(titular, saldo_inicial, tipo)
    
    def mostrar(self):
        print(f"  {self.titular}")
        print(f"   Tipo: {self.tipo}")
        print(f"   Saldo: ${self.saldo:,.2f}")

La clase CuentaBancaria se utiliza para crear cuentas de diferentes tipos. El __init__ asigna los valores de los atributos titular, saldo y tipo, que deberán ser pasados al crear la instancia.

Como se quiere que se pueda crear una cuenta a partir de una transferencia realizada, se crea el método de clase cuenta_desde_transferencia. Este método recibe el titular, monto y origen de la transferencia, y hace las modificaciones necesarias para convertir los datos en los atributos requeridos para crear la instancia.

Esta es finalmente creada llamando a cls con los parámetros correspondientes.

c1 = CuentaBancaria("Ana García", 1500)
c1.mostrar()

c2 = CuentaBancaria.cuenta_desde_transferencia("María Pérez", 2000, "pago nómina")
c2.mostrar()

La cuenta corriente es creada instanciando normalmente CuentaBancaria. La cuenta desde transferencia es creada llamando al método de clase cuenta_desde_transferencia desde la clase.

Resumen

El tipo de método a utilizar dependerá mayormente de los acceso que requiera para cumplir su función. Si utilizará y modificará los datos propios de la instancia entonces se deberá implementar un método de instancia.

Si los únicos datos de la clase requeridos por el método serán atributos de clase, entonces se puede utilizar un método de clase.

Finalmente, si no se utilizan atributos de la clase, se pueden utilizar métodos estáticos.

Método de instanciaMétodo de claseMétodo de estático
Primer parámetroselfclsNinguno
Acceso a instanciaSINONO
Modifica instanciaSINONO
Modifica claseSISINO
Llamada desde claseNOSISI
Llamada desde instanciaSISISI
UsosLógica que necesita datos de instanciaConstructores alternativos, patrones de diseñoValidaciones, conversiones, cálculos que no dependen de datos de instancia

En este artículo hemos visto los métodos estáticos y los métodos de clase y sus diferencias con los métodos de instancia. Hemos visto como implementarlos, sus usos más comunes, y cómo seleccionar correctamente el mejor método dependiendo del problema.

Espero que les sea de utilidad!


¿Querés aprender más? 📚

👉 Visitá nuestros cursos!
💬 Y si tenés dudas, o querés dejarnos tus comentarios sumate a la Comunidad JuncoTIC en Telegram!
¡Te esperamos!

Categorías: Programación

Andrea Navarro

- Ingeniera en Informática - Docente universitaria - Investigadora