Bienvenidos sean a este post, hoy hablaremos sobre dos conceptos importantisimos para trabajar con clases y objetos.
Los dos conceptos nacen de la relacion entre dos objetos donde la herencia aplica al tipo de relacion «ES-UN» (Is-A) lo cual equivale a que un objeto ES UN tipo del otro y la composicion por el tipo de relacion «TIENE-UN» (Has-A) donde un objeto se compone y TIENE UN tipo del otro, para dejarlo mas claro vamos a analizar el siguiente ejemplo, para ello crearemos un archivo llamado herencia.py y le agregaremos el siguiente codigo:
herencia.py
class Motor:
def inicio(self):
pass
def detener(self):
pass
class MotorElectrico(Motor):
pass
class MotorV8(Motor):
pass
class Auto:
clase_motor = Motor
def __init__(self):
self.motor = self.clase_motor()
def inicio(self):
print(
"Arrancando motor {0} para el auto {1}... Broom, broom!"
.format(self.motor.__class__.__name__,
self.__class__.__name__)
)
self.motor.inicio()
def detener(self):
self.motor.detener()
class AutoCarrera(Auto):
clase_motor = MotorV8
class AutoCiudad(Auto):
clase_motor = MotorElectrico
class AutoF1(AutoCarrera):
pass
auto = Auto()
autocarrera = AutoCarrera()
autociudad = AutoCiudad()
autof1 = AutoF1()
autos = [auto, autocarrera, autociudad, autof1]
for coche in autos:
coche.inicio()
Parece complicado pero no lo es tanto, vamos a analizar esta primera parte:
class Motor:
def inicio(self):
pass
def detener(self):
pass
class MotorElectrico(Motor):
pass
class MotorV8(Motor):
pass
En este caso creamos una clase que sera para la estructura del motor donde tenemos dos metodos para iniciar y detener, despues tenemos dos tipos de motor, estos casos aplican para la relacion «es un» porque estos dos motores son herederos de la clase padre si bien seran diferentes la base de ellos es establecida por Motor, esto es lo que llamamos herencia, vamos a analizar la siguiente clase:
class Auto:
clase_motor = Motor
def __init__(self):
self.motor = self.clase_motor()
def inicio(self):
print(
"Arrancando motor {0} para el auto {1}... Broom, broom!"
.format(self.motor.__class__.__name__,
self.__class__.__name__)
)
self.motor.inicio()
def detener(self):
self.motor.detener()
Esta es una clase parecida a Motor pero encarada para los Autos, porque esta nos servira como base para las proximas clases pero antes de hablar de ellas veamos a esta, primero crearemos una instancia de la clase Motor, luego tenemos un metodo __init__ para iniciar a la variable motor dentro de esta clase a traves de la instancia antes creada, esta es una relacion «tiene un» porque sabemos que el auto siempre TIENE UN motor pero despues puede variar el tipo de motor dependiendo del auto pero siempre tendra un motor, luego definimos un nuevo metodo llamado inicio, este lo usaremos para mostrar un mensaje en pantalla, en este caso lo haremos para mostrar tipo de motor y el tipo de auto en cuestion, esto por medio de format donde le pasaremos el valor del nombre de la clase Motor en el primer caso y en el segundo caso pasaremos el nombre del tipo de Auto, en realidad el nombre de la instancia que recibiremos, lo siguiente sera llamar al inicio del motor (como si arrancara un auto de verdad), por ultimo tenemos un metodo para detener el auto que lo unico que hace es llamar a la funcion de detener el motor, en todos los casos siempre enviamos a self para que siempre tome la instancia correcta, pasemos a ver el siguiente bloque de clases:
class AutoCarrera(Auto):
clase_motor = MotorV8
class AutoCiudad(Auto):
clase_motor = MotorElectrico
class AutoF1(AutoCarrera):
pass
En este caso crearemos tres clases nuevas pero de la misma forma que los motores porque tenemos dos tipos de autos nuevos que seran herederas de Auto pero tenemos una clase que no es heredera de Auto sino de AutoCarrera pero esta tambien hereda todo lo de Auto gracias a la clase que es heredera de Auto, en los dos primeros casos iniciamos a la variable clase_motor porque no tenemos ninguna definida pero en la ultima clase no lo haremos porque al ser heredera de AutoCarrera ya tiene esta variable iniciada y no es necesaria hacer esto, antes de finalizar vamos a hacer lo siguiente:
auto = Auto()
autocarrera = AutoCarrera()
autociudad = AutoCiudad()
autof1 = AutoF1()
autos = [auto, autocarrera, autociudad, autof1]
Primero crearemos una serie de objetos para los distintos tipos de autos que hemos creado en las clases anteriores, por ultimo creamos una lista de los objetos que generamos anteriormente, esto es un objeto iterable para el siguiente paso:
for coche in autos:
coche.inicio()
En este caso obtenemos todos los coches en la lista y por ultimo arrancamos a cada uno de ellos por medio de inicio, probemos de ejecutar y ver que sucede:
tinchicus@dbn001vrt:~/python$ python3 herencia.py
Arrancando motor Motor para el auto Auto... Broom, broom!
Arrancando motor MotorV8 para el auto AutoCarrera... Broom, broom!
Arrancando motor MotorElectrico para el auto AutoCiudad... Broom, broom!
Arrancando motor MotorV8 para el auto AutoF1... Broom, broom!
tinchicus@dbn001vrt:~/python$
Observen como mostramos todas los tipos de autos que disponemos y de cual motor esta compuesto, vean como el auto tiene un motor generico para el auto generico, luego vemos como el auto de carrera y de formula 1 poseen un motor V8 y en cambio el auto de ciudad posee un motor electrico, en todos los casos podemos ver como aplicamos herencia dado que los tipos derivados de la clase base reciben todas las propiedades y metodos, y para el caso de los motores es de composicion porque sabemos que todos los autos tienen motores pero despues diferenciamos dependiendo del tipo de auto, antes de terminar vamos a chequear si una clase pertenece a otra clase, para ello iremos al interprete de python e importaremos del archivo anterior tres clases:
>>> from herencia import Auto, AutoCarrera, AutoF1
Aqui importaremos las tres clases mencionadas, cuando lo ejecuten nos mostrara el resultado anterior y esto es debido a que tenemos el ciclo for para mostrar la salida, nuestro siguiente paso sera crear unos objetos basados en las clases importadas:
>>> auto = Auto()
>>> autocarrera = AutoCarrera()
>>> autof1 = AutoF1()
Aqui simplemente creamos tres objeto o instancias de las clases importadas, nuestro siguiente paso sera crear una lista de tuples de los objetos antes creados y una lista con las clases importadas mediante las siguientes lineas:
>>> autos = [(auto,'auto'), (autocarrera,'autocarrera'), (autof1,'autof1')]
>>> clases_autos = [Auto, AutoCarrera, AutoF1]
Hasta ahora venimos bien simple pero vamos a complicar un poco mediante el siguiente bucle o mejor dicho bucles:
>>> for coche, nombre_coche in autos:
... for clase in clases_autos:
... pertenece = isinstance(coche, clase)
... msj = 'es un' if pertenece else 'no es un'
... print(nombre_coche, msj, clase.__name__)
...
Primero tenemos un bucle que pasara por todos los elementos almacenados en autos, luego usaremos otro bucle que pasara por todas las clases almacenadas en clases_autos, primero crearemos una nueva variable llamada pertenece la cual almacenara el resultado del chequeo entre el valor almacenado en coche y clase para ver si coche es una instancia de clase, nos devolvera un True o False dependiendo del resultado de la verificacion, luego usamos una variable para almacenar un mensaje donde diremos que ‘es un’ si pertenece es True de lo contrario dira que ‘no es un’, por ultimo mostraremos el nombre del coche, seguido del resultado en msj y por ultimo el nombre del tipo de clase que estamos pasando, una vez terminado este ciclo ira al primer bucle y tomara el siguiente valor y repetira lo explicado anteriormente, esto se repetira hasta que termine la iteracion en autos, su salida sera la siguiente:
auto es un Auto
auto no es un AutoCarrera
auto no es un AutoF1
autocarrera es un Auto
autocarrera es un AutoCarrera
autocarrera no es un AutoF1
autof1 es un Auto
autof1 es un AutoCarrera
autof1 es un AutoF1
En esta salida podemos ver como auto (objeto) es un Auto (clase) pero auto no es ni un AutoCarrera ni un AutoF1 pero el siguiente auto (autocarrera) si es un Auto y un AutoCarrera pero no un AutoF1 y por ultimo AutoF1 es todas las clases, esto es debido a lo que comentamos anteriormente, un objeto que es heredero de otro no solamente hereda a su padre sino tambien al padre de su padre y asi hasta llegar a la clase base, la herencia es siempre hacia abajo porque esta clase puede ser el padre de otra clase pero nunca podra ser padre de una clase superior, vamos a crear un nuevo bucle for para chequear desde otro punto de vista:
>>> for clase1 in clases_autos:
... for clase2 in clases_autos:
... es_subclase = issubclass(clase1, clase2)
... msj ='{0} una subclase de'.format(
... 'es' if es_subclase else 'no es')
... print(clase1.__name__, msj, clase2.__name__)
...
Aca colectaremos todos los elementos de clases_autos por medio del primer bucle, luego haremos lo mismo pero lo almacenaremos en un iterador distinto, despues crearemos una nueva variable que almacenara el resultado de issubclass donde compara entre las dos clases si una es una subclase de la otra, despues tenemos un mensaje donde mostraremos si es o no es subclase, esto mediante format y un condicional donde verifica si es_subclase es True dejando el primer mensaje de lo contrario muestra el segundo, tal como en el bucle anterior, por ultimo mostramos si la clase1 es o no una subclase de clase2, veamos su salida:
Auto es una subclase de Auto
Auto no es una subclase de AutoCarrera
Auto no es una subclase de AutoF1
AutoCarrera es una subclase de Auto
AutoCarrera es una subclase de AutoCarrera
AutoCarrera no es una subclase de AutoF1
AutoF1 es una subclase de Auto
AutoF1 es una subclase de AutoCarrera
AutoF1 es una subclase de AutoF1
En este ejemplo vemos claramente lo que comentamos anteriormente con respecto a la herencia y como un objeto no solamente es heredero de su padre sino tambien del padre del padre.
En resumen, hoy hemos visto herencia, como es, para que nos sirve, cuales son las similitudes que mantiene con respecto a otros lenguajes, como se genera y como se define, tambien hemos visto como es la composicion, para que no es util y como se debe implementar, tambien hemos como debemos pensar para poder implementar a estas dos conductas en nuestros codigos, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.00
