¿Cómo son los métodos de una clase Python por defecto?

publicado por: Anonymous

Trasteando con Python, me he dado de bruces con un error tipo takes 2 positional arguments but 3 were given. Mi método, que está dentro de una clase, era de la forma

def add(x, y):
    return x +y

Acababa de leer la documentación básica sobre clases y conocía la existencia de los modificadores @classmethod y @staticmethod, que la documentación dice que son básicamente “azúcar sintáctico”. También sé que self o cls no son palabras reservadas, así que no importa si mi primer parámetro se llama self, cls o x.

El error obtenido me da por pensar que por defecto, los métodos de una clase son considerados métodos de clase (y el primer argumento es considerado una referencia a la clase/instancia) y si los quiero estáticos tengo entonces que poner el modificador, pero no he encontrado nada que lo indique explícitamente en la documentación.

Puede parecer obvio, pero para mí ha sido bastante contraintuitivo que si declaro

def saludar() :
   print("hola")

dentro de una clase, la llamada dará error porque la función tiene 0 parámetros pero intentaremos pasar uno (implícito) con la llamada. ¿Puede alguien confirmar que los métodos son por defecto de clase, como si estuvieran anotados con @classmethod?

solución

Los métodos de una clase, por defecto, funcionan de la siguiente forma:


Caso 1: Métodos de Instancia

Una clase sin @classmethod o @staticmethod. Como mencionas, self o cls no son palabras reservadas pero son las sugeridas para mantener un estándar en la Programación Orientada a Objetos en Python:

class Clase:
    def __init__(cualquiercosa, nombre):
        cualquiercosa.nombre = nombre

    def hola(cualquiercosa):
        print('¡Hola, %s!' % cualquiercosa.nombre)

Efectivamente, todos los métodos de una clase necesitan tener la referencia a la instancia. Como puedes apreciar ni siquiera estoy usando self.

>>> c1 = Clase('Diego')
>>> c1.hola()
¡Hola, Diego!

Esto es equivalente, aunque un poco tonto, pero demuestra la forma en que un método es llamada usando una instancia de la clase:

>>> c1 = Clase('Diego')
>>> Clase.hola(c1)
¡Hola, Diego!

No es posible llamar el método sin una instancia de por medio a menos que uses @classmethod como veremos en el Caso 2:

>>> Clase.hola()
Traceback (most recent call last):
  File "clases.py", line 11, in <module>
    Clase.hola()
TypeError: unbound method hola() must be called with Clase instance as first argument (got nothing instead)

Caso 2: Métodos de Clase

Una clase con @classmethod. Al decorar un método con @classmethod lo que le estás diciendo a Python es: “Python, por si acaso quiero usar ese método sin tener una instancia así que no me vengas con tonterías de self“. Y Python te responderá: “Ok, pero entonces dame una referencia a la clase”. Nuevamente, no es necesario usar cls:

class Clase:
    @classmethod
    def hola(cualquiertonteria, nombre):
        print('¡Hola, %s!' % nombre)

>>> Clase.hola('Diego')
¡Hola, Diego!

Esta clase ni siquiera tiene constructor, es una clase sin sentido que solo te saluda pero la puedes llamar sin necesidad de tener una instancia de la clase. Podrías hacer algo como esto:

class Clase:
    def __init__(cualquiercosa, nombre, rango='Cadete'):
        cualquiercosa.nombre = nombre
        cualquiercosa.rango = rango

    def hola(cualquiercosa):
        print('¡Hola, %s! Eres un %s' % (cualquiercosa.nombre, cualquiercosa.rango))

    @classmethod
    def desde_nombre(cualquiertonteria, nombre):
        return cualquiertonteria(nombre)

El método desde_nombre lo que hace realmente es retornar una instancia de la clase. En este caso no hay mucha ciencia pero es muy útil en casos complejos en los que los datos recibidos no concuerdan con los parámetros del constructor.

>>> c1 = Clase.desde_nombre('Diego')
>>> c1.hola()
¡Hola, Diego! Eres un Cadete

Caso 3: Métodos Estáticos

Clase con @staticmethod. En resumen lo que le dices a Python es “Python, voy a crear un método sin instancia y sin clase asi que no me vengas con tonterías de self o cls“. Y Python te responderá: “¿Sabes que eso lo puedes hacer sin necesidad de una clase, no?”. Un método con @staticmethod es prácticamente una forma elegante de llamar una función desde una clase, ya que no recibe ni la instancia, ni la clase misma.

Personalmente lo he usado pocas veces pero se me vienen casos en los que este método podría estar estrechamente relacionada a los datos que manejas en la clase y quieres mantener todo en el mismo sitio. Es decir, en vez de crear una función “helper” en otro módulo, pues lo creas en la misma clase como algún tipo de validación:

class Clase:
    def __init__(cualquiercosa, nombre):
        cualquiercosa.nombre = nombre

    def hola(cualquiercosa):
        print('¡Hola, %s!' % cualquiercosa.nombre)

    @staticmethod
    def mis_padres_estan_locos(nombre):
        estan_locos = False
        if nombre in ('Hitler', 'Kakaroto'):
            estan_locos = True
        return estan_locos

>>> Clase.mis_padres_estan_locos('Hitler')
True
>>> Clase.mis_padres_estan_locos('Diego')
False
>>> Clase.mis_padres_estan_locos('Kakaroto')
True

Esto es, en resumen, más o menos como funcionan las clases en Python.

Respondido por: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *