Comencemos esta sesión creando una clase para poder graficar puntos en un espacio de tres dimensiones, lo cual conseguimos escribiendo
#--------------------------------------------------------------------------------------
# Realizamos las importaciones necesarias para trabajar en esta sesión:
from mpl_toolkits import mplot3d # Módulo para trabajar con gráficas en 3 dimensiones
import matplotlib.pyplot as plt
import numpy as np
#--------------------------------------------------------------------------------------
# Creamos la clase Graficador3
class Graficador3():
# Constructor
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
# Creamos el método para graficar el punto
def graficar(self):
# Configuración de los ejes
axes = plt.axes(projection="3d")
axes.set_title("Punto en 3 dimensiones",fontsize=14,fontweight="bold")
axes.set_xlabel("X")
axes.set_ylabel("Y")
axes.set_zlabel("Z")
# Creamos la gráfica del punto
axes.scatter(self.x , self.y , self.z, color='blue')
# Mostramos la gráfica
plt.show()
posteriormente
# Creamos un objeto de la clase Graficador3
q1 = Graficador3(2, 3, 4)
# Invocamos el método Graficar()
q1.graficar()
Ahora bien, nos gustaría que a los puntos graficados en 3 dimensiones se les pueda calcular también la norma, entonces podemos pensar a la clase Punto3 (vista en la sesión anterior) como una subclase de Graficador3 consiguiendo así que el método graficar()
sea heredado a la subclase Punto3. No obstante, recordemos que la clase Punto3 es una subclase de Punto2, de modo que, al buscar que dicha clase sea también subclase de Graficador3, estamos pensando entonces en el concepto de herencia múltiple.
Hasta ahora, cuando trabajabamos con una superclase y subclase abordabamos el concepto de herencia simple, la cual consiste básicamente en que las subclases sólo tendrán una única superclase. Ahora bien, para trabajar con subclases que poseen más de una superclase estaremos utilizando herencia múltiple (así como indica la imagen anterior).
Para continuar recordemos el código de la superclase Punto2
# Superclase Punto2
class Punto2():
# Constructor
def __init__(self, x, y):
self.x = x
self.y = y
# __str__
def __str__(self):
return f'{self.x, self.y}'
# Método norma:
def auxiliar(self):
return self.x ** 2 + self.y ** 2
def norma2(self):
return np.sqrt(self.auxiliar())
y pasemos a modificar el código de la subclase Punto3 de modo que las clases Punto2 y Graficador3 sean sus respectivas superclases. Para ello
Al momento de definir la subclase Punto3 pondremos como parámetros los nombres de las superclases. Es decir, en vez de escribir class Punto3(Punto2)
como lo habíamos escrito en sesiones anteriores pondremos class Punto3(Punto2, Graficador3)
En herencia múltiple no utilizaremos el super().
para referenciar métodos de las superclases, pues en estos casos tendremos más de una superclase en cuestión. En su lugar usaremos nombre_de_la_superclase().
veamos pues la modificación pertinente del código
# Subclase Punto3 de las superclase Punto2 y Graficador3
class Punto3(Punto2, Graficador3):
# Constructor
def __init__(self, x, y, z):
# Hacemos referencia de los parámetros del método __init__ de la superclase
# Punto2:
Punto2.__init__(self, x, y)
# Atributo propio de la subclase Punto3
self.z = z
# __str__
def __str__(self):
# Hacemos referencia de la salida del método __str__ de la superclase
# Punto2:
return f'({Punto2.__str__(self)},{self.z})'
# Método norma
def auxiliar(self):
return Punto2.auxiliar() + self.z ** 2
def norma3(self):
return np.sqrt(self.auxiliar())
luego, creamos un objeto de la clase Punto3 y vemos las característica que ha heredado de las dos superclases
# Creamos un objeto de la clase Punto3
p1 = Punto3(2,1,2)
# Invocamos el método graficar() de la superclase Graficador3
p1.graficar()
# Invocamos el método __str__
print(p1)
# Invocamos el método norma3
print(p1.norma3())
((2, 1),2) 3.0
con lo cual hemos conseguido el resultado que buscabamos. Para continuar abordemos otro sencillo ejemplo, donde crearemos las siguientes clases
# Superclase 1
class Figura():
# Constructor
def __init__(self, ancho, altura):
self.ancho = ancho
self.altura = altura
# ---------------------------------------
# Superclase 2
class Color():
# Constructor
def __init__(self, color):
self.color = color
las cuales abordan las dimensiones del ancho
y altura
de una figura geométrica (en la clase Figura) y el color (en la clase Color). Posteriormente crearemos la clase Cuadrado como subclase de las clase Figura y Color como sigue
# Subclase Cuadrado de las superclases Figura y Color
class Cuadrado(Figura, Color):
# Constructor
def __init__(self, lado, color):
# Sobreescritura del método __init__ correspondiente a la clase Figura
Figura.__init__(self, lado, lado)
# Sobreescritura del método __init__ correspondiente a la clase Color
Color.__init__(self, color)
donde el parámetro de lado
del método __init__
de la clase Cuadrado jugará tanto en el papel del parámetro ancho
como el papel del parámetro altura
de la clase Figura. De tal manera
# Creamos un objeto de la clase Cuadrado
c1 = Cuadrado(2, 'Verde')
# Veamos algunas características heredadas de la clase Figura
print(f'Ancho: {c1.ancho}')
print(f'Altura: {c1.altura}')
print()
# Veamos algunas características heredadas de la clase Color
print(f'Color: {c1.color}')
Ancho: 2 Altura: 2 Color: Verde
Finalmente, podemos agregar un método en la clase Cuadrado para calcular el área:
# Subclase Cuadrado de las superclases Figura y Color
class Cuadrado(Figura, Color):
# Constructor
def __init__(self, lado, color):
# Sobreescritura del método __init__ correspondiente a la clase Figura
Figura.__init__(self, lado, lado)
# Sobreescritura del método __init__ correspondiente a la clase Color
Color.__init__(self, color)
# Método para calcular el área
def area(self):
return self.ancho * self.altura
donde podemos observar que escribimos self.ancho * self.altura
en vez de intentar escribir self.lado * self.lado
. La razón radica en que tanto self.ancho
como self.altura
son atributos heredados de la clase Figura y el nombre de lado
sólo hace alusión al nombre del parámetro que pasamos en el constructor de la clase Cuadrado. Además, podemos ver fácilmente que dicho constructor no posee atributos propios, por lo que, en este caso, sería totalmente erróneo pensar en self.lado
. Luego
# Creamos un objeto de la clase Cuadrado
c2 = Cuadrado(4, 'Azul')
# Invocamos el método area()
c2.area()
16