Diamond problem en Python



@eduardo_gpg

Número de visitas 716

Tiempo de lectura 4 min

21 Abril 2023

Hace un par de días tome un pequeño examen de que tanto conocía la programación orientada a objetos en Python. Y una de las preguntas que se me hizo super interesante era sobre cómo poder solventar el Diamond problem con este lenguaje.

Este es un concepto que en lo particular no escucho tanto. Inclusive yo lo paso por alto cuando me ha tocado explicar POO con Python. Sin embargo se me hizo un tema super interesante para compartir un una entrega de PyWombat.

Es por ello que, en este nuevo post, me gustaría hablaremos acerca del DiamondProblem, qué es, cómo se presenta y por supuesto, cómo podemos solucionarlo. Creo será un post sumamente interesante, así que te invito a que te quedes.

Bien, sin más introducción, comencemos con el post.

Herencia múltiple en Python

Antes de entrar de lleno en materia hagamos un pequeño repaso en Python, recordando que, a diferencia de otros lenguajes de programación, en Python es posible implementar la herencia múltiple. Esto quiere decir que una misma clase puede heredar atributos y métodos de una N cantidad de clases padres.

Esto la verdad esta bastante cool.

Para poder implementar la herencia múltiple, basta con colocar dentro de los paréntesis, separados por una coma, las clases padres de las que deseamos heredar.

Aquí un ejemplo.

class Animal():
    def comer(self):
       print('El animal come')

class Mascota():
    def dormir(self):
       print('La mascota duerme')

class Perro(Animal, Mascota):
   pass

>>> loki = Perro()
>>> loki.comer()
El animal come

>>> loki.dormir()
La mascota duerme

Para este ejemplo mi clase Perro hereda tanto de la clase Animal como de la clase Mascota, pudiendo así acceder a sus respectivos atributos y métodos.

Este es un ejemplo bastante sencillo, sin embargo recuerda que puedes heredar de una N cantidad de clases, basta con colocarlos dentro de los paréntesis las clases Padres y separarlas por una coma.

Diamond Problem

Ahora que ya sabemos más acerca de la herencia múltiple en Python, es momento de hablar del problema que nos atañe el día hoy, el diamond problem.

Verá, este problema ocurre cuando una clase hereda de 2 o más clases que poseen uno o múltiples métodos con el mismo nombre y la clase hija hace uso de dichos métodos.

En este caso, al los nombres de los métodos repetirse, puede ocurrir una ambigüedad en cuanto a qué método se debe usar, ya que hay múltiples definiciones del mismo.

A este problema se le conoce como "Diamente" ya que la herencia múltiple puede crear una estructura en forma de diamante en el árbol de herencia, donde una clase hereda de 2 que a su vez heredan de una clase común.

Aquí mi aporte con una representación visual.

Esto puede sonar algo confuso, así que lo veamos un par de ejemplos.

Continuemos con nuestro código de mascotas.

class Animal:
    def comer(self):
        print("El animal come.")

class Mamifero(Animal):
    def comer(self):
        print("El mamífero come.")

class Mascota(Animal):
    def comer(self):
        print("La mascota come.")

class Perro(Mamifero, Mascota):
    pass

>>> loki = Perro()
>>> loki.comer()
???

Para este nuevo ejemplo ya nos complicamos un poco más, ya que ahora todas las clases padres de Perro están implementando su propio método comer. Un método que varía su comportamiento dependiendo de clase.

Aquí la gran pregunta es ¿Qué mensaje veremos en consola si mi objeto de tipo perro ejecuta el método comer? Acaso séra: ¿La mascota come, El mamífero come o El animal come?

Bueno, la unica forma de saberlo es ejecutándo nuestro código.

>>> loki.commer()
"El mamífero come."

Para este ejemplo vemos en consola “El mamífero come”, Pero ¿A qué se debe esto? Bueno, esto se debe a cómo Python funciona. Verás, cuando implementamos la herencia múltiple existe un tipo de regla sobre que métodos y atributos deben ser usados primero. En Python a esta regla se le denomina MRO, o por sus siglas al español: Método de resolución de orden de la clase.

La regla es la siguiente: En Python, primero se van a buscar los métodos o atributos en las clase principal (En la clase de la cual se instancio el objeto) en mi caso será la clase **Perro.**** Si los atributos o método no existen dentro de esta clase, entonces se procede a buscar dentro de las clases padres, leyendo de izquierda a derecha en el orden de herencia.

Al mi clase Perro no poseer el método comer ,Python comenzará la búsqueda de dicho método en las clases padres. Como la jerarquía de Herencias es de Izquierda a Derecha, Python le dará prioridad a buscar el método comer dentro de la clase Mamífero (Primera clase a la izquierda) y después en la clase Mascota.

Como la clase Mamífero si posee el método comer la búsqueda finaliza y se ejecuta el respectivo método.

Ahora, compliquemos aun más nuestro código, indiquemos que la clase Mascota y Animal son las únicas que poseen el método **comer.****

class Animal:
    def comer(self):
        print("El animal come.")

class Mamifero(Animal):
    pass

class Mascota(Animal):
    def comer(self):
        print("La mascota come.")

class Perro(Mamifero, Mascota):
    pass

>>> loki = Perro()
>>> loki.comer()
???

¿Ahora cual crees que será la salida en consola? ¿El animal come o la mascota come?

Bueno, siguiendo con la regla mencionada anteriormente, el resultado en consola será: La mascota comer.

>>> loki.commer()
"La mascota come."

Ya que, primero Python busca en la clase Perro, al no encontrar el método comienza su busqueda en las clases padre, primero en **Mamífero** y después en *Mascota.*

Pero ¿ Y si ningunas de las clases padres poseer el método o atributo a buscar?

Ejemplo.

class Animal:
    def comer(self):
        print("El animal come.")

class Mamifero(Animal):
    pass

class Mascota(Animal):
    pass

class Perro(Mamifero, Mascota):
    pass

>>> loki.commer()
"El animal come"

Bueno, en ese caso, al no encontrarse ningún método o atributo en la clase principal o clases padres, Python comienza su búsqueda en las clases padres de los padres, subiendo un nivel en la jerarquía. Estos va a suceder hasta que se encuentre el atributo o método, o simplemente ninguna de las clases en el árbol genealógico posea esa característica y Python deba lanzar una excepción.

Es por ello que es de suma importancia definir correctamente el orden de las herencias. No se trata de colocar las clases padres en el orden que deseemos, estas deben tener un orden definido y un por qué de dicho orden. Esto permitirá evitar problemas de ambigüedad y reducir comportamientos inesperados en nuestro código.

Cómo solventar este problema.

Existen un par de estrategias para solventar este problema, aquí te listo las que considero pueden sernos de utilidad.

1.- Definir correctamente el orden de jerarquía al heredar. Como ya lo mencione anteriormente hay que saber porqué una clase debe heredar antes que otra.

2.- Utilizar interfaces o clases abstractas. En lugar de herencia múltiple, recomiendo interfaces o clases abstractas, para simplemente definir que se debe hacer, pero no el cómo.

3.- Utilizar composición. En lugar de heredar directamente, podemos añadir funcionalidades nuevas o extenderlas con la composición.

4.- Evitar hacer uso de herencia múltiple. Sí, así se sencillo.


¿El contenido te resulto de ayuda?

Para poder dejar tu opinión es necesario ser un usuario autenticado. Login

Más Tips y Ejercicios 🐍

Adquiere una subscripción PyWombat por tan solo $3 USD. al mes.

Conoce los beneficios de ser usuario premium:
Niveles desbloqueados: Ten accesos a todos los niveles de ejercicios. 🔓
Nuevo límite: Incrementa tu límite de ejercicios por semana. 🚀
Contenido único: Recibe semanalmente recursos exclusivos de Python (Videos, Artículos y Capitulos del libro PyWombat, comienza como desarrollador Python. 🐍