En Programación Orientada a Objetos la Herencia es el mecanismo que permite crear nuevas clases a partir de las preexistentes.
La herencia nos permite crear otras clases sin tener que empezar de cero. Utilizamos una clase padre con las características de base y le agregamos las características particulares a la clase hija.
La ventaja de la herencia es la reutilización de código.
La herencia nos permite modelar otros sistemas sin tener que empezar de cero, utilizamos una clase Padre con las características de base y le agregamos las características particulares a la clase Hija.
Cuando una clase hereda de otra, hereda todos sus métodos y atributos.
La clase hija hereda los métodos y atributos de la clase padre pero además puede definir sus propios métodos y atributos.
Ejemplo 1
La clase Figura es la clase padre que tiene tres clases hijas: Rectangulo, Circulo, Hexagono.
Ejemplo 2
La clase Pet es la clase padre que tiene dos clases hijas: Dog, Cat.
Ejemplo 3
La clase Vehiculo es la clase padre que tiene varias clases hijas: Moto, Coche, Furgoneta, Camion, Bici.
class Vehiculo(): # primero va el constructor con las propiedades
def __init__(self, marca, modelo): # marca y modelo nos permiten dar un estado inicial a los objetos que hereden de Vehiculo
self.marca = marca # la marca que pasemos como parámetro al constructor será nuestra self. marca del objeto
self.modelo = modelo # el modelo que pasemos como parámetro al constructor será nuestro self. modelo
self.enmarcha = False # inicialmente los objetos creados no estarán en marcha
self.acelera = False # inicialmente los objetos creados no estarán acelerando
self.frena = False # inicialmente los objetos creados no estarán frenando
def arrancar(self): # método. Los métodos nos dan el comportamiento del objeto
self.enmarcha=True
def acelerar(self): # método. Estos métodos permiten cambiar las propiedades definidas en el constructor
self.acelera = True
def frenar(self): # método
self.frena = True
def estado(self):
print(f"Marca: {self.marca} \nModelo: {self.modelo} \
\nEn marcha: {self.enmarcha} \nAcelerando: {self.acelera} \nFrenando: {self.frena}")
class Moto(Vehiculo): # Así indicamos que la clase Moto hereda de Vehiculo
pass # No añadimos nada para comprobar que Moto goza de las propiedades y métodos de Vehiculo
miMoto=Moto("Honda", "Rebel") # creamos una instancia que hereda tb el constructor por lo que se han de pasar marca y modelo
miMoto.estado() # al heredar de Vehiculo puedo usar los métodos heredados
Marca: Honda Modelo: Rebel En marcha: False Acelerando: False Frenando: False
Una clase que hereda de otra adquiere sus métodos y propiedades, pero a su vez puede tener sus propios métodos y propiedades.
Pensemos en el comportamiento que pueda tener una moto que no tengan todos los vehículos, por ejemplo, hacer el caballito. La variable será hcaballito (hacer el caballito).
class Vehiculo():
def __init__(self, marca, modelo):
self.marca = marca
self.modelo = modelo
self.enmarcha = False
self.acelera = False
self.frena = False
def arrancar(self):
self.enmarcha = True
def acelerar(self):
self.acelera = True
def frenar(self):
self.frena = True
def estado(self):
print(f"Marca: {self.marca} \nModelo: {self.modelo} \
\nEn marcha: {self.enmarcha} \nAcelerando: {self.acelera} \nFrenando: {self.frena}")
class Moto(Vehiculo):
hcaballito = "" # creamos la nueva variable: haciendo el caballito
def caballito(self): # creamos el método caballito
self.hcaballito="Voy haciendo el caballito" # el método modifica el valor de la variable hcaballito
miMoto=Moto("Honda", "Rebel")
miMoto.caballito() # invocamos el nuevo método
miMoto.estado() # no da error, pero no informa de que estamos haciendo el caballito
# será necesario sobrescribir el método estado
Marca: Honda Modelo: Rebel En marcha: False Acelerando: False Frenando: False
Ahora un objeto de tipo moto puede usar seis métodos que son los cinco heredados (incluyendo el construcctor) y el suyo propio.
class Vehiculo():
def __init__(self, marca, modelo):
self.marca = marca
self.modelo = modelo
self.enmarcha = False
self.acelera = False
self.frena = False
def arrancar(self):
self.enmarcha = True
def acelerar(self):
self.acelera = True
def frenar(self):
self.frena = True
def estado(self):
print(f"Marca: {self.marca} \nModelo: {self.modelo} \
\nEn marcha: {self.enmarcha} \nAcelerando: {self.acelera} \nFrenando: {self.frena}")
class Moto(Vehiculo):
hcaballito = ""
def caballito(self):
self.hcaballito = "Voy haciendo el caballito"
def estado(self): # = nombre y nº de parámetros que el método sobrescrito de la clase padre
print(f"Marca: {self.marca} \nModelo: {self.modelo} \
\nEn marcha: {self.enmarcha} \nAcelerando: {self.acelera} \nFrenando: {self.frena}\
\n{self.hcaballito}")
miMoto=Moto("Honda", "Rebel")
miMoto.caballito() # invocamos el nuevo método y ahora si vemos que hace el caballito
miMoto.estado() # si no hubiéramos invocado el método caballito no se imprimiría Voy...
print("="*30)
miCoche=Vehiculo("VW", "Golf") # creamos un vehículo que no sea una moto
miCoche.estado() # al invocar su estado se aplica el estado de la clase Vehiculo
print("="*30)
motoClasica=Moto("Harley-Davidson", "Fat Boy") # creamos una moto con la que no haremos el caballito
motoClasica.estado() # al invocar su estado se aplica el estado de la clase Moto
# pero previamente no hemos invocado el método caballito()
Marca: Honda Modelo: Rebel En marcha: False Acelerando: False Frenando: False Voy haciendo el caballito ============================== Marca: VW Modelo: Golf En marcha: False Acelerando: False Frenando: False ============================== Marca: Harley-Davidson Modelo: Fat Boy En marcha: False Acelerando: False Frenando: False
Cuando escribimos miMoto.estado() surge una pregunta: ¿Estamos llamando al método estado de la clase Vehiculo o al método estado de la clase Moto?
El método estado de la clase Moto sobrescribe (anula, invalida) el método estado de la clase Vehiculo.
Invocar o no el método caballito es opcional, si no se invoca no se imprime la frase 'Voy haciendo el caballito', simplemente se imprime "" que es el valor de la variable hcaballito cuando el método no actúa.
Supongamos que creamos la clase Quad que hereda de la clase Moto y esta a su vez hereda de la clase Vehiculo. Esto es lo que se llama una cadena de herencias o jerarquía de herencias.
La clase Quad heredaría el método estado de la clase Moto y no el método estado de la clase Vehiculo ya que Quad hereda directamente de Moto.
Si creamos la clase Furgoneta lo lógico es que herede de la clase Vehiculo y no de la clase Moto.
La clase Furgoneta tendrá un nuevo comportamiento (método) que será la capacidad de cargar (llevar carga).
class Vehiculo():
def __init__(self, marca, modelo):
self.marca = marca
self.modelo = modelo
self.enmarcha = False
self.acelera = False
self.frena = False
def arrancar(self):
self.enmarcha = True
def acelerar(self):
self.acelera = True
def frenar(self):
self.frena = True
def estado(self):
print(f"Marca: {self.marca} \nModelo: {self.modelo} \
\nEn marcha: {self.enmarcha} \nAcelerando: {self.acelera} \nFrenando: {self.frena}")
class Furgoneta(Vehiculo): # creamos la clase Furgoneta que hereda de la clase Vehiculo
def cargada(self, carga): # la variable carga será True o False
self.carga = carga
if self.carga:
return "La Furgoneta está cargada" # en este método usamos un return para informar
else:
return "La Furgoneta no está cargada"
miFurgoneta=Furgoneta("Nissan","N100")
miFurgoneta.arrancar()
miFurgoneta.estado()
print(miFurgoneta.cargada(True)) # usamos un print ya que el método 'cargada' se creó con un reuturn
Marca: Nissan Modelo: N100 En marcha: True Acelerando: False Frenando: False La Furgoneta está cargada