owned this note
owned this note
Published
Linked with GitHub
# PRO-UT2-A4 Herencia múltiple
## Herencia múltiple en Python
Python permite herencia múltiple. Se utiliza cuando la clase hija tiene características de **más de una clase madre**. En este caso, la clase hija hereda los miembres de más de una clase madre:
```python
class MotherClass1:
pass
class MotherClass2:
pass
class ChildClass(MotherClass1, MotherClass2):
pass
```
Ejemplo:
```python
class SeaAnimal:
def __init__(self, name):
self.name = name
def swim(self):
return "Swimming"
class LandAnimal:
def __init__(self, name):
self.name = name
def walk(self):
return "Walking..."
class Penguin(SeaAnimal, LandAnimal):
def __init__(self, name):
super().__init__(name)
```
En el caso anterior un Pingüino **es un** animal marino y **es un** animal terrestre y puede, por tanto, nadar y caminar.
```python
linux = Penguin("Tux")
print(linux.walk()) # Walking...
print(linux.swim()) # Swimming
```
## Orden de resolución
Al heredar de más de una clase madre se puede dar el caso de que dichas clases madres tengan métodos con el mismo nombre:
```python
class B:
def x(self):
print('x: B')
class C:
def x(self):
print('x: C')
class D(B, C):
pass
d = D()
d.x()
```
El resultado de ejecutar el programa anterior es:
```
x: B
```
Python resuelve este caso siguiendo las siguientes reglas:
* Si el método ha sido sobreescrito en la clase hija, este es el que se ejecuta.
* Si el método no existe en la clase hija pero si en más de una de las clases madres se ejecuta el de aquella que se haya especificado antes a la hora de heredar.
Lo mismo pasa si usamos para llamar usando `super()` un método que existe en más de una clase madre, se resuelve ejecutando el de aquella que se haya indicando que **hereda en primer lugar** (más a la izquierda):
```python
class B:
def x(self):
print('x: B')
class C:
def x(self):
print('x: C')
class D(B, C):
def x(self):
super().x()
pass
d = D()
d.x() # 'x: B'
```
## Usando métodos de más de una clase madre
Cómo vimos, el método `super()` nos perminte acceder a métodos de la clase madre, pero si tenemos más de un antecesor con **métodos con el mismo nombre**, debemos disponer de una forma de discriminar de cuál de ellos queremos reutilizar un miembro. Para ello reemplazamos el método `super()` por el nombre de la clase de la que queremos reutilizar el método:
```python
class B:
def x(self):
print('x: B')
class C:
def x(self):
print('x: C')
class D(B, C):
def x(self):
C.x(self) # Se ha de pasar self al método
pass
d = D()
d.x() # x: C
```
Además, cuando invocamos de esta forma un método de una de las clases madres hemos pasar como primer parámetro del mismo `self` para que la clase madre disponga del objeto sobre el que aplicar el método.
El funcionamiento es el mismo para cualquier método, en particular también para el constructor:
```python
class SeaAnimal:
def __init__(self, name):
print("SeaAnimal INIT")
self.name = name
def swim(self):
return "Swimming..."
class LandAnimal:
def __init__(self, age):
print("LandAnimal INIT")
self.age = age
def walk(self):
print("Walking...")
class Penguin(SeaAnimal, LandAnimal):
def __init__(self, name, age):
SeaAnimal.__init__(self, name=name)
LandAnimal.__init__(self, age=age)
penguin = Penguin("Kowalski", 3)
print(penguin.name, penguin.age)
```
### Actividad 1
Crea la clase `CocheHibrido` que hereda de las clases `CocheCombustion` y `CocheElectrico` que vimos en la actividad anterior
```python
class Vehiculo:
def __init__(self, matricula, color, num_ruedas):
self._matricula = matricula
self._color = color
self._num_ruedas = num_ruedas
def get_matricula(self):
return self._matricula
def __str__(self):
return f"Vehículo con matrícula {self.get_matricula()}, de color {self._color} y {self._num_ruedas} ruedas."
class CocheCombustion(Vehiculo):
def __init__(self, matricula, color, num_ruedas, cilindrada):
super().__init__(matricula, color, num_ruedas)
self._cilindrada = cilindrada
def get_cilindrada(self):
return self._cilindrada
def descripcion(self):
return f"Coche con motor de combustión de {self.get_cilindrada()} cc, matrícula {self.get_matricula()}, de color {self._color} y {self._num_ruedas} ruedas."
class CocheElectrico(Vehiculo):
def __init__(self, matricula, color, num_ruedas, potencia):
super().__init__(matricula, color, num_ruedas)
self._potencia = potencia
def get_potencia(self):
return self._potencia
def descripcion(self):
return f"Coche con motor eléctrico de {self.get_potencia()}CV, matrícula {self.get_matricula()}, de color {self._color} y {self._num_ruedas} ruedas."
class Moto(Vehiculo):
def descripcion(self):
return f"Moto, matrícula {self.get_matricula()}, de color {self._color} y {self._num_ruedas} ruedas."
```
Reescribe los métodos de la clase `CocheHibrido` de forma que el resultado de los mismos sea coherente. Aprovecha los métodos de las clases madre siempre que sea posible.
Inserta el resultado:
```python
class Vehiculo:
def __init__(self, matricula, color, num_ruedas):
self._matricula = matricula
self._color = color
self._num_ruedas = num_ruedas
def get_matricula(self):
return self._matricula
def __str__(self):
return f"Vehículo con matrícula {self.get_matricula()}, de color {self._color} y {self._num_ruedas} ruedas."
class CocheCombustion(Vehiculo):
def __init__(self, matricula, color, num_ruedas, cilindrada):
Vehiculo.__init__(self, matricula, color, num_ruedas)
self._cilindrada = cilindrada
def get_cilindrada(self):
return self._cilindrada
def descripcion(self):
return f"Coche con motor de combustión de {self.get_cilindrada()} cc, matrícula {self.get_matricula()}, de color {self._color} y {self._num_ruedas} ruedas."
class CocheElectrico(Vehiculo):
def __init__(self, matricula, color, num_ruedas, potencia):
super().__init__(matricula, color, num_ruedas)
self._potencia = potencia
def get_potencia(self):
return self._potencia
def descripcion(self):
return f"Coche con motor eléctrico de {self.get_potencia()}CV, matrícula {self.get_matricula()}, de color {self._color} y {self._num_ruedas} ruedas."
class Moto(Vehiculo):
def descripcion(self):
return f"Moto, matrícula {self.get_matricula()}, de color {self._color} y {self._num_ruedas} ruedas."
class CocheHibrido(CocheCombustion, CocheElectrico):
def __init__(self, matricula, color, num_ruedas, cilindrada, potencia):
CocheCombustion.__init__(self, matricula, color, num_ruedas, cilindrada)
self._potencia = potencia
def __str__(self):
result = super().__str__()
result += f"\n Una cilindrada de {self._cilindrada} CC y una potencia de {self._potencia} CV"
return result
ch = CocheHibrido("1234GHJ", "Gris", 4, 1500, 230)
print(ch)
```
### Actividad 2
La clase `Venta` tiene las siguientes propiedades:
* Atributos:
* Lista de listas con datos de la compra de la forma `[['peras', 2],['manzanas', 1.5], ['tomates', 3]]` en la que el primer elemento es el nombre del producto y el segundo la cantidad
* Métodos:
* `__init__()` se le pasa una lista de artículos
* `__str__()` muestra resumen de la lista de artículos
* `agregar_pedido(producto, cantidad)` se añade el producto a la lista si no existe. Si existe se incrementa la cantidad:
* `modificar_pedido(producto, cantidad)` se modifica la cantidad de un producto si está en la lista.
* `quitar_pedido(producto)` se elimina el producto de la lista.
* `_obtener_cantidad(producto)` se le pasa el nombre de un producto y devuelve la cantidad del mismo
La clase `Productos` tiene las siguientes propiedades:
* Atributos:
* Lista de listas de artículos con su precio de la forma `[['aguacates', 2.67],['manzanas', 1.13],['peras', 1.43],['tomates', 0.80]]` en la que el primer elemento es el producto y el segundo su precio
* Métodos:
* `__init__()` se le pasa una lista artículos con su precio
* `__str__()`
* `cambiar_precio(producto, precio)`
* `quitar_producto(producto)` se elimina un producto de la lista de productos
* `obtener_precio(producto)`
* `_existe_producto(producto)` devuelve True si el producto está en la lista y False en caso contrario.
La clase `Factura` hereda de `Productos` y de `Venta` y tiene las siguientes propiedades:
* Atributos:
* Lista con datos de la compra
* lista con precios de los artículos
* nif del cliente
* Métodos:
* Los heredados de las clases **Madre**
* `__init__()` recibe todos los atributos. Aprovecha constructores `Productos` y `Venta`
* `_generar_encabezado()` devuelve HTML con fila de encabezado con columnas **Producto | Cantidad | Precio unitario | Precio total**
* `_generar_fila(producto, cantidad, precio)` devuelve HMLT con fila con dato de uno de los productos de la factura
* `_calcular_total()` devuelve el total de la factura
* `_generar_total()` devuelve HTML con la fila que muestra el total de la misma.
* `mostrar_factura()` devuelve tabla HTML completa con los datos de la compra. Aprovecha los métodos `_generar_encabezado(), _generar_fila() y _generar_total()`
###### tags: `pro` `ut2` `herencia` `múltiple` `oop` `poo`