Creación de subclases

Una vez que hemos creado una clase, es posible crear otra clase a partir de ella (una "subclase") que "herede" las características -atributos y métodos- de la clase original (a la que, en estas circunstancias, se denomina "superclase" o "clase padre").

Por ejemplo, sigamos trabajando con la clase que nos permitía crear círculos:

class Circulo:
    
    pi = 3.141592
    
    def __init__(self, radio):
        self.radio = radio
    
    def area(self):
        return Circulo.pi * (self.radio ** 2)

Ésta va a ser nuestra clase "padre" -la superclase- a partir de la que crear las clases "hijas" o subclases. Como sabemos ya, podríamos instanciar un objeto de esta clase con el código:

c = Circulo(3)

Para crear una subclase basta con definir una nueva clase pasando como argumento el nombre de la clase padre. Por ejemplo, supongamos que queremos crear un tipo especial de círculo que tenga un color asociado:

class CirculoColoreado(Circulo):
    
    pass

Vemos que estamos creando una clase a la que llamamos CirculoColoreado que se va a basar en la clase Circulo. Si quisiéramos poder inicializar el estado de nuestro "círculo coloreado" tendríamos que añadir un método constructor:

class CirculoColoreado(Circulo):
    
    def __init__(self, color):
        self.color = color

Ahora ya podríamos crear un "círculo coloreado" pasando el color correspondiente:

c = CirculoColoreado("azul")
c.color

'azul'

Eso sí, con el código que define nuestra subclase no estamos previendo la posibilidad de definir el radio del círculo. Podríamos invocar el método .area(), pero obtendríamos un error de tipo AttributeError ya que no se ha inicializado dicho atributo:

c.area()

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-12-795dd763e280> in <module>
----> 1 c.area()

<ipython-input-1-b881104cc864> in area(self)
      7 
      8     def area(self):
----> 9         return Circulo.pi * (self.radio ** 2)

AttributeError: 'CirculoColoreado' object has no attribute 'radio'

Lo que debemos hacer es, cuando instanciamos nuestro círculo coloreado invocando al constructor de la subclase, invocar también el constructor de la clase padre, de la siguiente forma:

class CirculoColoreado(Circulo):
    
    def __init__(self, radio, color):
        self.color = color
        super().__init__(radio)

Como podemos comprobar, además de crear el atributo color, estamos invocando el método constructor de la clase padre mediante la instrucción super().__init__() pasando los argumentos necesarios -en este caso el radio del círculo-.

La instanciación del objeto exige ahora pasar dos argumentos, el radio y el color:

c = CirculoColoreado(5, "azul")

Y ahora ya tenemos acceso a los atributos radio y color de nuestro objeto:

c.radio

5

c.color

'azul'

...así como al método area:

c.area()

78.5398