Fundamentos de POO para interfaces gráficas en Python

Herencia y mixins

La herencia de clases es la capacidad que tiene una clase, llamada subclase, de heredar los atributos y métodos de una o varias clases, llamadas superclases.

Muchas veces para explicar este concepto he usado de ejemplo una clases Vehículo que sirve como base de dos clases Coche y Camión. O también una clase Animal como base para dos clases Gato y Perro. Todos los vehículo tienen ruedas, todos los animales tienen patas, etc. Se trata de encontar atributos comunes en las clases padre y hacer que las clases hijas los hereden, bla, bla, bla.

Pero sinceramente, en todos los años que llevo programando, que son como 12 o 13, sólo he diseñado un sistema alrededor de este concepto y ni siquiera salió bien. No me malinterpretéis, no quiero decir que la herencia sea una tontería, sino que raramente es aplicable para resolver problemas del mundo real. Donde sale a relucir su verdadero potencial es en la ingeniería de software, cuando se utiliza para crear bibliotecas que tienen como objetivo desarrollar más software, como por ejemplo los frameworks web, los motores de videojuegos y las interfaces gráficas, que son entornos puramente virtuales.

Fuera de estos ámbitos sólo hay una razón por la que la herencia me parece útil, y esa es es para extender las funcionalidades de las clases heredando de otras clases. Estas clases se conocen como Mixins y tienen la peculiaridad de ofrecer una funcionalidad pero por si mismas no sirven para nada.

Imaginad que tenemos una clase A y otra clase B sin nada en especial:

mixins.py

class A:
    pass


class B:
    pass

Nuestro objetivo es implementar en ellas un nuevo método llamado instancia() que muestre por pantalla su posición en la memoria.

Usando un mixin esto sería muy fácil de solucionar, simplemente crearemos una clase que haga eso mismo y haremos que A y B hereden de ella para extender su comportamiento:

class MixinInstancia:
	def instancia(self):
		print(hex(id(self)))

class A(MixinInstancia):
    pass


class B(MixinInstancia):
    pass

a = A()
a.instancia()

b = B()
b.instancia()

Pero no nos quedemos aquí. Imaginad que necesitamos otra función común para mostrar por pantalla el nombre de la clase, pues podríamos declarar otro mixin y heredar de él, dando como lugar al concepto de herencia múltiple:

class MixinInstancia:
    def instancia(self):
        print(hex(id(self)))


class MixinClase:
    def clase(self):
        print(type(self).__name__)


class A(MixinInstancia, MixinClase):
    pass


class B(MixinInstancia, MixinClase):
    pass


a = A()
a.instancia()
a.clase()

b = B()
b.instancia()
b.clase()

Como véis usando mixins podemos extender nuestras clases de una forma flexible, sin obligar a que una clase tengan todas las funcionalidades de otra por el hecho de heredar de ella, algo que puede ocasionar problemas trabajando con herencia múltiple.

Hablaremos de ello en la siguiente lección.