Clases y objetos en Python III

 Algunos métodos especiales fundamentales.

Dentro de los métodos especiales podemos destacar a los siguientes como los mas relevantes:

 __bool__
__format__
__hash__
__init__
__new__
__repr__
__str__

Método especial __str__.

El método especial STR devuelve una cadena de texto con la representación del objeto tal cual como se haya especificado en la cadena devuelta por el método. Si se invoca la función PRINT sin haber definido en el método __str__ de la clase el formato de devolución entonces se mostrará por pantalla la ubicación en memoria de dicho objeto, ejemplo:

Contenido del objeto Auto1:
<__main__.Vehiculo object at 0x0000021AAF00BFD0>

sin embargo, al definir la representación del objeto en un formato de cadena en particular, éste aparecerá con el formato especificado, facilitando así la lectura al invocarse a función PRINT pasando como argumento el objeto en cuestión. Ejemplo:


import os
os.system("cls")

class Vehiculo:
        def __init__(self,nombre,color,marca,modelo) -> None:
                self.nombre=nombre
                self.color=color
                self.marca=marca
                self.modelo=modelo
       
        def __str__(self) -> str:
               return f'Objeto de la clase Vehículo: {self.nombre}, {self.color}'

Auto1 = Vehiculo('Herbie','Blanco','Volkswagen','Beetle')

print (f'Contenido del objeto Auto1:')
print(Auto1)

sCadena = str(Auto1)
print(sCadena)


Resultado:

Contenido del objeto Auto1:
Objeto de la clase Vehículo: Herbie, Blanco
Objeto de la clase Vehículo: Herbie, Blanco

Nótese como ahora ha aparecido el texto "Contenido del objeto Auto1:" antes de la data seleccionada dentro del método STR. Esto es debido al formato que se le ha dado tras la instrucción RETURN, de igual manera solo se han escogido 2 propiedades del objeto Vehiculo para que aparezca en la cadena de texto devuelta al imprimirse en dicho objeto o al pasarse como argumento a la función STR.

Herencia

Hemos podido comprobar hasta ahora la utilidad del uso de objetos en Python, sin embargo su poder va mucho mas allá al permitir además la herencia de clases. La definición de herencia en POO no es mas que la técnica que permite la creación de una clase especializada a partir de una clase mas genérica,  haciendo que el código sea mas legible y organizado y ahorrando la escritura de líneas de código de manera significativa ya que facilita muchísimo la reutilización de código. Otra manera de ver la herencia, y es como se va a ejemplificar a continuación, es la creación de una clase a partir de otra ya definida. La nueva clase va a heredar las propiedades y métodos de la case original. Es el mismo concepto de herencia en la vida real y manteniendo la estructura de jerarquías donde el abuelo esta por encima que el padre y a su vez el padre esta por encima del hijo, siendo que los que están por debajo heredan de los que estan directamente por encima, salvo que en informática por suerte no es necesario que nadie fallezca para poder heredar de las clases padres.

Criterios para aplicar correctamente la herencia en Python siguiendo el ejemplo de los vehículos

  1. Determinar características en común de todos los objetos: por ejemplo marca y modelo.
  2. Determinar comportamientos en común de todos los objetos: por ejemplo encender y apagar.
  3. Englobar todas las características y comportamientos en común en una clase padre o superclase.
  4. Determinar las características y comportamientos particulares para construirlos dentro de cada objeto en particular.

Veamos el siguiente ejemplo:

import os
os.system("cls")

class Automovil:
        def __init__(self,nombre,color,marca,modelo) -> None:
                self.nombre=nombre
                self.color=color
                self.marca=marca
                self.modelo=modelo        
        def __str__(self) -> str:
                return f'''
Datos del automovil:
Nombre: {self.nombre}
Color: {self.color}
Marca: {self.marca}
Modelo: {self.modelo}'''

class Motocicleta(Automovil):
        def __str__(self) -> str:
                return f'''
Datos de la motocicleta:
Nombre: {self.nombre}
Color: {self.color}
Marca: {self.marca}
Modelo: {self.modelo}'''

Auto1 = Automovil('Herbie','Blanco','Volkswagen','Beetle')
Moto1 = Motocicleta('Terminator 2','Negro','Harley Davidson','Fat Boy')

print(Auto1)
print(Moto1) 

Resultado:

Datos del automovil:
Nombre: Herbie
Color: Blanco
Marca: Volkswagen
Modelo: Beetle

Datos de la motocicleta:
Nombre: Terminator 2
Color: Negro
Marca: Harley Davidson
Modelo: Fat Boy

Como podemos ver hemos creado una clase llamada Automovil en la cual se han definido ciertas propiedades, luego se ha creado una clase llamada Motocicleta a partir de la clase Automovil, eso se logra al colocar entre paréntesis el nombre de la clase de la cual se van a heredar las propiedades y métodos. En este caso hemos colocado el nombre de la clase Automovil para este fin. Evitando así la necesidad de reescribir las propiedades y métodos para esta nueva clase Motocicleta. Como es obvio para que esto tenga un sentido práctico ambas clases deben compartir atributos comunes, en este caso tanto las motos como los automóviles comparten los atributos nombre, color, marca y modelo. Sin embargo hay un detalle. En el método especial __str__ el formato de cadena que se definió en la case Vehiculo aparece en el encabezado la frase "Datos del automóvil:" y esto no sería conveniente al momento de mostrar la cadena representativa del objeto Motocicleta ya que parecería una incongruencia del programa. Para solventar esto, basta con definir un método especial __str__ dentro de la definición de la clase Motocicleta y de esta manera Python ejecutará el método declarado dentro de esta nueva clase y no el heredado de su clase 'padre', como podemos ver en el ejemplo anterior.

Como nota final de este apartado y quedando pendiente para una futura actualización, convendría modificar el código para hacerlo mas óptimo creando una clase 'padre' llamada Vehiculo que contenga las propiedades genéricas de su clase (marca, modelo, color, año de fabricación, etc), definiendo luego las clases que hereden dichos atributos pero que además se incluyan un atributo llamado tipo, permitiendo definir el tipo de vehículo al que se refiere (automovil, motocicleta, camión, bicicleta, aeronave, etc). Esta sería la aproximación mas fiel a la filosofía de la POO en general.

Veamos otro ejemplo de la creación de varias instancias de clases. En este caso vamos a usar el código que crea una clase llamada automovil dándole algunas características predefinidas tales como color, puertas, marca, modelo, peso, largo, ancho y estado. Con este código vamos a crear 2 instancias de la clase automovil, una llamada auto1 y la otra llamada auto2. ambas van a tener las mismas propiedades de inicio pero al auto2 lo vamos a arrancar a través de su método del mismo nombre.

class automovil():
        color='Rojo'
        puertas='5'
        marca ='Ford'
        modelo='Focus'
        peso=1384
        largo=4.38
        ancho=1.82
        estado='Detenido'

        def arrancar(self):          
                print('Arrancando')    
                self.estado='Rodando'

auto1=automovil()

auto2=automovil()

auto2.arrancar()

print('El largo del auto1 es de: ', auto1.largo)
print('El largo del auto2 es de: ', auto2.largo)
print('El ancho del auto1 es de: ', auto1.ancho)
print('El ancho del auto2 es de: ', auto2.ancho)
print(f'El vehículo auto1 está en este momento: {auto1.estado}')
print(f'El vehículo auto2 está en este momento: {auto2.estado}')

Como podemos comprobar ambos objetos o instancias de clase poseen las mismas características o propiedades excepto por el estado, el cual ha sido modificado por la ejecución del método arrancar del objeto auto2.

El código anterior puede ser mejorado cambiando el tipo de dato de la propiedad estado a booleano, ya que el vehículo en este nivel de detalle que estamos manejando solo puede tener 2 estados, detenido o rodando. Otra mejora que se le puede hacer al código anterior es hacer que se pueda imprimir el estado del vehículo en la misma llamada al procedimiento arrancar devolviendo en el RETURN una cadena que indique siestado actual: Rodando o Detenido según sea el caso. También conviene crear un método que muestre por pantalla las propiedades del vehículo. Veamos como quedaría:

class automovil():
        color='Rojo'
        puertas='5'
        marca ='Ford'
        modelo='Focus'
        peso=1384
        largo=4.38
        ancho=1.82
        estado=False

        def arrancar(self,modo):
                if modo:
                        self.estado=True
                        return 'Rodando'
                else:
                        self.estado=False
                        return 'Detenido'
        def info(self):
                print('* * * * * Información del vehículo * * *')
                print('Marca: ',self.marca)
                print('Modelo: ',self.modelo)
                print('Puertas: ',self.puertas)
                print('Color: ',self.color)
                print('Peso: ',self.largo)
                print('Largo: ', self.largo)
                print('Ancho: ', self.ancho)
                if self.estado:
                        print('Estado: Rodando')
                else:
                        print('Estado: Detenido')
               

auto1=automovil()

auto2=automovil()

print('El auto1 esta ahora: ',auto1.arrancar(False))
print('El auto2 esta ahora: ', auto2.arrancar(True))

auto1.info()
auto2.info()


Resultado:

El auto1 esta ahora:  Detenido
El auto2 esta ahora:  Rodando
* * * * * Información del vehículo * * *
Marca:  Ford
Modelo:  Focus
Puertas:  5
Color:  Rojo
Peso:  4.38
Largo:  4.38
Ancho:  1.82
Estado: Detenido
* * * * * Información del vehículo * * *
Marca:  Ford
Modelo:  Focus
Puertas:  5
Color:  Rojo
Peso:  4.38
Largo:  4.38
Ancho:  1.82
Estado: Rodando

Como podemos comprobar ahora el propio método arrancar admite un cambio de estado a través de un parámetro booleano y devuelve el estado en que se encuentra actualmente en caso se desee mostrar por pantalla como vemos en el ejemplo.Igualmente podemos comprobar como ambas instancias de clase poseen las mismas propiedades.

Comentarios

Entradas populares