Herencia y Polimorfismo en Python
El uso de la Herencia y Polimorfismo en Python se implementa en distintas plataformas y servicios. Estos pueden ser juegos, formularios, aplicaciones ejecutables, etc.. En nuestro blog encontrará la categoría donde se encuentran los distintos módulos con distintas aplicaciones y ejemplos.
Table of Contents
ToggleHerencia en Python
La herencia se aplica en el momento en que una clase B hereda de una clase A. Por lo tanto, aquélla conserva todos los métodos y atributos de la misma (A). Por ejemplo:
class ClaseA:
def __init__(self):
self.a = 1
class ClaseB(ClaseA):
pass
mi_objeto = ClaseB()
print(mi_objeto.a)
Out:
1
En este código, mi_objeto es una instancia de ClaseB, que no ha definido ningún atributo a. Sin embargo, lo hereda de la ClaseA. La ClaseB podría definir nuevos atributos y funciones, e incluso reemplazar a los de su clase padre. Como se muestra a continuación:
class ClaseA:
def __init__(self):
self.a = 1
class ClaseB(ClaseA):
def __init__(self):
self.a = 3
mi_objeto = ClaseB()
print(mi_objeto.a)
Out:
3
En este caso, definimos el método de inicialización __init__() y creamos un nuevo atributo b. No obstante, esto reemplaza la antigua definición de __init__() en ClaseA de modo que, por ejemplo, self.a no se habrá definido. Siempre que reemplazamos un método, para permitir que las funciones con el mismo nombre en las clases padres se ejecuten, debemos emplear la función super() de la siguiente manera:
class ClaseA:
def __init__(self):
self.a = 1
class ClaseB(ClaseA):
def __init__(self):
self.a = 2
super().__init__()
mi_objeto = ClaseB()
print(mi_objeto.a)
Out:
1
Aspectos Generales
A continuación, a modo de ejemplo, veremos algunos aspectos que suelen prestarse para confusión con respecto al uso de herencias en Python.
Primer ejemplo:
class Animales:
def habla(self):
print(«yo soy un animal»)
class gato(Animales):
pass
objeto = gato()
objeto.habla()
Out:
yo soy un animal
Aquí la clase gato hereda el método habla() de la clase Animales, entonces al crear el objeto “este_objeto” con la clase gato, se ejecuta dicha función.
Segundo ejemplo:
class Animales:
def habla(self):
print(«yo soy un animal»)
def descripcion(self):
print(f»Yo soy una {self.insecto}»)
class gato(Animales):
pass
class hormiga(Animales):
def __init__(self):
self.insecto = «Hormiga»
objeto = gato()
objeto.habla()
otro_objeto = hormiga()
otro_objeto.descripcion()
nuevo_objeto = hormiga()
print(nuevo_objeto.insecto)
Out:
yo soy un animal
Yo soy una Hormiga
Hormiga
Como podemos ver, el método descripcion() no está dentro de la clase hormiga, sin embargo esta hereda la clase Animales. Por tanto, también heredará la función descripción() cuyo atributo es “insecto” y el valor especificado con nuevo_objeto es “Hormiga”.
Uso de super()
class Animales():
def __init__(self, nombre):
self.nombre = nombre
class Perro (Animales):
def __init__(self, sonido):
self.sonido = sonido
objeto = Perro(«perro», «Guaaaoo!»)
print(objeto.nombre)
Podríamos suponer que debería funcionar con normalidad. No obstante, dicho código nos devolverá un mensaje de “TypeError…” ¿A qué se debe esto? Bueno, como vimos anteriormente, hay métodos que no se heredan del todo y pueden generar ese tipo de errores. Entonces deberemos usar nuevamente super() para heredar “del todo” dichos métodos. Por ejemplo:
class Animales():
def __init__(self, nombre):
self.nombre = nombre
class Perro (Animales):
def __init__(self, nombre, sonido):
super().__init__(nombre)
self.sonido = sonido
objeto = Perro(«perro», «Guaaaoo!»)
print(objeto.nombre)
print(objeto.sonido)
Out:
perro
Guaaaoo!
Herencia Múltiple
Como hemos mencionado, Python posee la capacidad de heredar métodos y atributos de una clase padre. Esto también funciona para más de dos o tres clases, solo que una de estas hereda de varias clases padres. Por tanto, hay que tener en cuenta que una clase puede heredar de una cantidad arbitraria, indicándolas en la definición separadas por comas, tal como se haría con los argumentos. Debemos tener en cuenta que el orden de resolución se emplea de derecha a izquierda.
Herencia de clases con atributos:
class ClaseA:
def __init__(self):
self.a = 1
class ClaseB:
def __init__(self):
self.a = 2
class ClaseC(ClaseA, ClaseB):
pass
mi_objeto = ClaseC()
print(mi_objeto.a)
Out:
1
Como vemos la ClaseC() hereda el atributo a de sus clases padres, pero, como la resolución ocurre de derecha a izquierda, el atributo ClaseA.a (1) reemplaza a ClaseB.a (2).
Herencia de clases con Métodos:
class ClaseA:
def __init__(self):
self.a = 1
def dato(self):
print(«Este es un ejemplo de la clase A.»)
class ClaseB:
def __init__(self):
self.a = 2
def dato(self):
print(«Este es un ejemplo de la clase B.»)
class ClaseC(ClaseA, ClaseB):
pass
mi_objeto = ClaseC()
mi_objeto.dato()
Out:
Este es un ejemplo de la clase A.
Polimorfismo en Python
El polimorfismo es aquella aplicación que nos facilita invocar un determinado método de un objeto, y que podrá obtenerse distintos resultados según el mismo. Esto se debe a que distintos objetos (que pueden o no ser creados con la misma clase) pueden tener un método con un mismo nombre, pero que realicen distintas operaciones.
Ejemplo:
class Animales():
def __init__(self, mensaje):
self.mensaje = mensaje
def sonido(self):
print(self.sonido)
class Perro(Animales):
def sonido(self):
print(«Yo soy un perro»)
perro = Perro(«guao!»)
perro.sonido()
Out:
Yo soy un perro
¿Por qué nos devuelve el mensaje “Yo soy un perro” y no “guao!”? Ya que hemos especificado la función __init__() en la clase padre Animales() y luego, con el objeto perro, le hemos pasado el valor de self.sonido cuyo atributo es “guao!”. Esto se debe a que Python interpreta que, al crear un método con el mismo nombre, nos imprimirá en pantalla dicho valor “modificado” y no el de la clase padre Animales().
En otras palabras, lo que ocurre es una SUSTITUCIÓN de métodos, en este caso, el de la clase padre (Animales) por el de la clase hijo (perro).
Conclusión
La herencia y el polimorfismo en Python se establecen como una cuestión de organización para el programador que trabaja con muchos objetos y métodos. Permitiendo así, que el desarrollador no deba escribir, pensar y recordar muchos nombres de métodos diferentes, sino que pueda llamar al método del objeto adecuado con el mismo nombre que llamaría a otros.
¿Qué hay con respecto a la Abstracción y Encapsulación?
Podríamos decir que la Abstracción se utiliza para esconder detalles de errores no deseados mientras genera otros esenciales. En otras palabras, se usa para generar clases y métodos los cuales no generan instancias, sino que se utilizan como base para otras subclases. De esta forma, nos ahorramos la reutilización de código y/o métodos.
Con respecto a la Encapsulación, se la denomina una «buena práctica» por parte del programador, ya que busca ocultar código y datos «privados» dentro de una sola unidad. Esto en Python no suele tener mucha relevancia.