Herencia múltiple en Python, ¡el orden importa!
Hola. En este artículo vamos a ver la importancia de definir bien la herencia múltiple, esto es, el orden en el cual se llama a los constructores (__init__, que no es el constructor, aunque sí es el sitio donde escribiremos el código que ejecutaría el constructor) de las clases heredadas, ya que es fundamental, así como la importancia de nombrar bien a los atributos, para que no se sobreescriban (override); en Python no hay polimorfismo entendido como sobrecarga de métodos, aunque hay maneras de emularlo.
NOTA: Si se quiere entender con más profundidad como Python resuelve el tema de la herencia múltiple aconsejo que miréis el tema de MRO en http://www.python.org/download/releases/2.3/mro/.
Comenzamos...
Imaginemos que declaramos dos clases que heredan de object: clase1 y clase2. Creamos una tercera clase, clase3, que hereda de clase1 y clase2. Tal como aparece en la imagen de abajo:
NOTA: Si se quiere entender con más profundidad como Python resuelve el tema de la herencia múltiple aconsejo que miréis el tema de MRO en http://www.python.org/download/releases/2.3/mro/.
Comenzamos...
Imaginemos que declaramos dos clases que heredan de object: clase1 y clase2. Creamos una tercera clase, clase3, que hereda de clase1 y clase2. Tal como aparece en la imagen de abajo:
Al instanciar la clase3 vemos que ha heredado los métodos de las clases clase1 y clase2 pero únicamente el atributo de la clase1. ¿Por qué? Si nos damos cuenta en la clase3 no hemos llamado a ningún constructor, esto es, hacemos pass, por tanto Python resuelve por el orden en el que se ha declarado la herencia múltiple en clase3, es decir class clase3(clase1, clase2). Obtiene el atributo de la primera clase que se encuentra de izquierda a derecha, esto es, clase1.
Ahora si en el constructor de la clase3 llamamos a los constructores de las clases clase1 y clase2 tenemos que:
clase3 hereda tanto los métodos como los atributos de las clases clase1 y clase2. Esto es lo que esperábamos.
Ahora lo que vamos a hacer es renombrar los atributos de la clase1 y clase2 con el mismo nombre, es decir, van a tener un atributo cada una de las dos clases, que se va a llamar "atributo". Mira lo que pasa ahora:
¡Vaya! ¿Python no sabe distinguir entre dos atributos con el mismo nombre? La respuesta es que no sabe. ¿Y cual elige? Pues el atributo que sea el último en inicializarse. Como clase3 la hemos inicializado primero con el constructor de la clase1 y por último con el de la clase2, será el atributo de la clase2 el que existirá (esto es, hace un override al atributo de la clase1). Hacemos la prueba llamando a los métodos que nos devuelvan el valor de los atributos de las clases:
Y tenemos:
Es decir, el método devolver_atributo2() hace lo correcto, pero el método devolver_atributo1() lo que hace es devolver el atributo que está inicializado, esto es, el que se inicializó el último, es decir, el de la clase clase2. ¡Vaya! ¡Parece un lío, pero no lo es! Si lo piensas bien tiene su lógica (aunque no tanto su gracia).
¿No me crees? Vamos a hacerlo al revés. Ahora vamos a llamar a los constructores de la clase3 cambiando el orden, de manera que ahora llamamos primero al constructor de la clase2 y por último al constructor de la clase1. Mira lo que pasa:
Y tenemos que:
...como cabría esperar.
Tan solo con nombrar bien a los atributos de las clases heredadas todo funciona a las mil maravillas:
Y el resultado es:
Vamos a ver un último caso. Creamos una tercera clase, llamada clase25, que también hereda de object, y vamos a modificar clase3, que ahora va a heredar de clase25, clase1 y clase2. Todo queda como sigue:
Como vemos todo es normal; clase3 hereda tanto métodos como atributos. Pero... ¿y si cambiamos la forma en la que heredan las clases? Vamos a declarar que ahora clase25 hereda de clase2 y que clase3 ahora solo hereda de clase25 y clase1. ¡PUFF! ¡Vaya comida de cabeza!. Mira la siguiente imagen:
¿Ein? El lector, si ha entendido (que seguro que sí) lo expuesto en este post debe saber porqué no aparece el atributo2 si la clase clase25 hereda de clase2, que contiene dicho atributo. Evidentemente, porque en el constructor de la clase clase25 no se ha llamado al constructor de la clase clase2. Si hacemos la llamada entonces clase3 tiene todos los atributos que se esperaba heredar:
Bueno, espero que este artículo os sirva de ayuda a la hora de utilizar la herencia múltiple, con cuidado.
Saludos.
Hello Friend...Excellnt, Very illustrative...!!! Thnks.
ResponderEliminar...
Pls, can you tell me the name of your editor...?...i see intellisense...which is very useful!!!
...
my email: mmhgarciagmail.com
Thank You!!!
Muy bien, me ha quedado claro, muchas gracias!
ResponderEliminar