Atributos y métodos

Como ya vimos que algo que ilustra el potencial de la POO (Programación Orientada a Objetos) esa es la capacidad de definir variables y funciones dentro de las clases, aunque aquí se conoce como atributos y métodos respectivamente.

Atributos

A efectos prácticos los atributos no son muy distintos de las variables, la diferencia fundamental es que sólo existen dentro del objeto.

 

Atributos dinámicos

Dado que Python es muy flexible los atributos pueden manejarse de distintas formas, por ejemplo se pueden crear dinámicamente (al vuelo) en los objetos.

class Masita:
    pass
 
masita = Masita()
masita.sabor = "salado"
masita.color = "marrón"
 
print(f"El sabor de esta masita es {masita.sabor} "
      f"y el color {masita.color}")
 
El sabor de esta masita es Salado y el color Marrón
 

Atributos de clase

Aunque la flexibilidad de los atributos dinámicos puede llegar a ser muy útil, tener que definir los atributos de esa forma es tedioso. Es más práctico definir unos atributos básicos en la clase. De esa manera todas las masitas podrían tener unos atributos por defecto:

class Masita:
    chocolate = False
 
masita = Masita()
 
if masita.chocolate:
    print("La masita tiene chocolate")
else:
    print("La masita no tiene chocolate")
La masita no tiene chocolate

 

Luego podemos cambiar su valor en cualquier momento:

masita.chocolate = True
 
if masita.chocolate:
    print("La masita tiene chocolate")
else:
    print("La masita no tiene chocolate")
La masita tiene chocolate

Por lo menos de esta forma nos aseguraremos de que el atributo chocolate existe en todas las masitas desde el principio. Además, es posible consultar el valor por defecto que deben tener las masitas haciendo referencia al atributo en la definición de la clase:

print(Masita.chocolate)
False

 

Lo curioso es que si cambiamos ese atributo de clase (que no de objeto) a True, las siguientes masitas se crearán con chocolate, es decir, habremos modificado las instrucciones de creación de los objetos:

class Masita:
    chocolate = False
 
Masita.chocolate = True
 
masita = Masita()
 
if masita.chocolate:
    print("La masita tiene chocolate")
else:
    print("La masita no tiene chocolate")
La masita tiene chocolate

 

Métodos

Si por un lado tenemos las "variables" de las clases, por otro tenemos sus "funciones", que evidentemente nos permiten definir funcionalidades para llamarlas desde las instancias.

Definir un método es bastante simple, sólo tenemos que añadirlo en la clase y luego llamarlo desde el objeto con los paréntesis, como si de una función se tratase:

class Masita:
    chocolate = False
 
    def saludar():
        print("Hola, soy una masita muy sabrosa")
 
masita = Masita()
masita.saludar()
 
----------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-27-74df06911b9b> in <module>()
    6 
    7 masita = Masita()
----> 8 masita.saludar()
 
TypeError: saludar() takes 0 positional arguments but 1 was given

 

Sin embargo, al intentar ejecutar el código anterior desde una masita veréis que no funciona. Nos indica que el método saludar() requiere 0 argumentos pero se está pasando uno.

¿Cómo puede ser? Si en ningún momento hemos enviado ninguna información a masita...

Lo que tenemos aquí es la diferencia fundamental entre métodos de clase y métodos de instancia.

Probad a ejecutar el método llamando a la clase en lugar del objeto:

class Masita:
    chocolate = False
 
    def saludar():
        print("Hola, soy una masita muy sabrosa")
 
Masita.saludar()
Hola, soy una masita muy sabrosa
¡Ahora sí ha funcionado! ¿Cómo es posible? Y más importante,
¿por qué al llamarlo desde el objeto dice que estamos enviando un argumento? Veamos
este tema en la a continuación.