Bienvenidos a otra entrada dedicada a este maravilloso lenguaje de programacion, en este caso vamos a hablar de la función super en python 3. Siguiendo con programacion orientada a objetos como recordaras estuvimos viendo clases, herencia simple y múltiple.
Función super en python 3
Esta función nos permite invocar y conservar un método o atributo de una clase padre (primaria) desde una clase hija (secundaria) sin tener que nombrarla explícitamente. Esto nos brinda la ventaja de poder cambiar el nombre de la clase padre (base) o hija (secundaria) cuando queramos y aún así mantener un código funcional, sencillo y mantenible.
Herencia simple de clase y atributos en python con super()
Supongamos que queremos que una clase hija o secundaria herede los atributos de clase primaria o padre. Si nosotros no recurrimos a la función super, o llamamos al constructor init especificando los atributos. Deberemos reescribirlos, lo cual en una clase por ejemplo con 20 atributos seria una perdida de tiempo enorme! Fíjate en el ejemplo de abajo:
Valla bodrio. Esto podemos también podemos hacerlo así, llamando al constructor de la clase padre especificandola:
#!/usr/bin/env python # -*- coding: utf-8 -*- #https://pythones.net class Padre(object): #Creamos la clase Padre def __init__(self, ojos, cejas): #Definimos los Atributos self.ojos = ojos self.cejas = cejas class Hijo(Padre): #Creamos clase hija que hereda de Padre def __init__(self, ojos, cejas, cara): #creamos el constructor de la clase especificando atributos Padre.__init__(self, ojos, cejas) #Especificamos la clase y llamamos a su constructor + Atributos self.cara = cara #Especificamos el nuevo atributo para Hijo Tomas = Hijo('Marrones', 'Negras', 'Larga') print (Tomas.ojos, Tomas.cejas, Tomas.cara)
Resultado:
Marrones Negras Larga
O podemos hacerlo, utilizando super(). De esta forma es casi el mismo código pero no necesitamos especificar la clase padre, por lo que podremos cambiarle el nombre en cualquier momento y nuestro código seguirá funcional, fíjate:
De estas ultimas dos formas llamamos al Padre de la clase Hijo para no perder su código y ademas agregamos un atributo nuevo “cara” para la clase Hija. Recomiendo en caso de herencia simple utilizar siempre super en python
Ademas, prueba en este código cambiar el nombre Padre, por nose.. Abuelo y veraz que Hijo hereda igual los atributos de la clase padre o base / primaria!
Bueno que mas da, lo hago yo:
Veamos otro ejemplo en este caso heredando de list un método.
Llamar un método de otra clase padre con super() en python
#!/usr/bin/env python # -*- coding: utf-8 -*- # class Agregarelemento(list): #Creamos una clase Agregarelemento heredando atributos de clase list (clase incorporada) def append(self, alumno): #Definimos que el método append (de listas) añadirá el elemento alumno print (("Añadido el alumno"), (alumno)) #Imprimimos el resultado del método super().append(alumno) #Incorporamos la función super SIN INDICAR LA CLASE ACTUAL, seguida #del método append para la variable alumno Lista1 = Agregarelemento() #Definimos la clase de nuestra lista llamada "Lista1" Lista1.append ('Matias') #Añadimos un elemento a la lista como lo haríamos normalmente print (Lista1) #Imprimimos la lista para corroborar que se añadió el alumno
Resultado:
Añadido el alumno Matias
[‘Matias’]
En este caso cada vez que se añada un elemento a la lista se imprimirá el elemento que se añadió porque así lo definimos (mediante un print) en la clase hija y mediante la función super logramos ejecutar el método (“append“) padre de la clase incorporada list.
Espero haber logrado explicarme correctamente. Pero sino aquí te doy lata nuevamente. El método append pertenece a la clase incorporada list (que es normalmente utilizado para añadir un elemento a una lista).
Pero nosotros logramos añadirlo a una clase hija permitiéndonos así incorporar otro comportamiento adicional que es aplicable a cualquier otra lista a la que se le asigne esta clase hija. Y aunque cambiáramos el nombre de la clase hija (en este caso Agregarelemento ) el código funcionaria igual, fíjate:
#!/usr/bin/env python # -*- coding: utf-8 -*- #https://pythones.net class Agregar(list): #Cambiamos el nombre de la clase por "Agregar" def append(self, alumno): #Creamos método append print (("Añadido el alumno"), (alumno)) #Imprimimos el resultado super().append(alumno) #Escala a la clase padre "list" a por el método append con el parametro "alumno" Lista1 = Agregar() #Asignamos a la lista la clase "Agregar" (Instancia) Lista1.append ('Matias') #Llamamos el método append de Lista1 con el argumento 'Matias' print (Lista1) #Imprimimos Lista1 que es una lista obviamente
Resultado:
Añadido el alumno Matias
[‘Matias’]
Como ves cambiamos solo el nombre de la clase y el método append funciona igual porque super en python 3 permite delegar la llamada del método directamente a la clase padre (principal o superclase) sin especificar en el la clase hija. Este es uno de los beneficios de super en la versión de python 3!
Super en python y Herencia múltiple
Aquí es donde super se vuelve emm.. “super(?)” .
En la herencia múltiple de clases es donde podemos explotar al máximo la función super. Vamos a ver otro ejemplo de herencia múltiple y cómo podemos utilizar la función super en el para llamar métodos de clases padres o bases.
Para este ejemplo vamos a crear la siguiente estructura de clases:
Como ves en el gráfico (horrible pero útil) la clase Shepadoodle hereda de las clases Caniche y Pastor_Aleman que a su vez heredan de la clase principal Perros (Flechas negras para la herencia de clases). Las flechas rojas nos muestran los métodos de cada una.
Dentro de clase principal Perros también tenemos ladrar y grunir solo que se me olvidó incluirlo en el gráfico!
Ok listos? Ahora revisa el código donde en el método xladrar utilizamos la función super para multiplicar el ladrido!
#!/usr/bin/env python # -*- coding: utf-8 -*- # class Perros(object): #Declaramos la clase principal Perros def ladrar (self): print ("""GUAAAUU GUAAAUU!""") def grunir (self): print ("""GRRRRRR GRRRRR""") class Caniche (Perros):#La clase secundaria hereda de la clase principal perros def ladrar(self): print ("""guau guau guau""") def grunir(self): print ("""gññññiii gñññiiii""") class Pastor_Aleman(Perros):#La clase secundaria hereda de la clase principal perros def ladrar(self): print ("""GuaUUU GUAAAUUU GuaUUU""") def grunir(self): print ("Agrfgregreff aggrrfsgrrr") class Shepadoodle (Caniche, Pastor_Aleman):#La clase hereda de las clases hijas de su padre Perros def ladrarx(self, veces): for cuantas in range(veces): super(Shepadoodle, self).ladrar() Tommy = Pastor_Aleman() Piny = Caniche() Cuchele = Shepadoodle() Cuchele.ladrarx(5) # Imprime guau guau guau (5 veces) porque heredo el ladrido de la clase padre CANICHE #Pero si eliminamos o renombramos el método ladrar de CANICHE que imprimiria? #Imprimiria el ladrido del Pastor_Aleman #Y si borramos ambos? Imprimirá el ladrido de Perros!
Como ves en la explicación que incluí en los comentarios del código, si nosotros borramos o renombramos el método del cual super está heredando continuará buscando automáticamente los métodos de las clases superiores.
Ha y por cierto Shepadoodle es una mezcla de caniche y pastor alemán.
Pero porque si nosotros borramos el método “ladrar” de Caniche imprime el de Pastor_Aleman y no recurre a la clase padre Perros?
MRO (Method Resolution Order)
Porque la función super sigue un árbol de ancestros que se conoce como MRO (Method Resolution Order) que podría traducirse a Resolución de ordenes de métodos.
Como decía la función super busca siguiendo el orden (MRO) delegando en la primer clase superior por encima de la clase a la que pertenece hasta encontrar el método que estamos indicando. Y siguiendo la secuencia de MRO comienza por buscar desde la clase donde se encuentra la invocación (función super) al método, sus padres, los padres de sus padres y así hasta alcanzar la clase Object, en caso de no encontrarlo antes obviamente.
Fíjate tu mismo con el código que hicimos de ejemplo de ir borrando el método de las clases padres de abajo hacia arriba y ve comprobando cómo interpreta el orden. Si pruebas cambiar el orden de las clases en:
class Shepadoodle (Caniche, Pastor_Aleman):#La clase hereda de las clases hijas de su padre Perros def ladrarx(self, veces): for cuantas in range(veces): super(Shepadoodle, self).ladrar() #POR: class Shepadoodle (Pastor_Aleman, Caniche):#La clase hereda de las clases hijas de su padre Perros def ladrarx(self, veces): for cuantas in range(veces): super(Shepadoodle, self).ladrar()
Verás cómo prefiere el método de la clase Pastor_Aleman antes que la de Caniche.
Esto nos está diciendo que el orden de las clases, importa!
La función super nos va a permitir crear subclases que componen a otras determinando el comportamiento según el orden que le demos a las mismas.
Lo siento, tenia que ponerlo!!
Herencia múltiple y atributos con super en python?
Si hasta ahora vimos como heredar atributos de clases con Herencia Simple y como heredar Métodos con Herencia Múltiple. Pero que pasa con los atributos en la Herencia Múltiple?. Pues super() no nos sirve en ese caso. Debemos llamar a los constructores de ambas clases especificandolas por su nombre. Y si cambiamos el nombre u orden de la clase deberemos especificarlo!
#!/usr/bin/env python # -*- coding: utf-8 -*- #https://pythones.net class Padre(object): #Creamos la clase Padre def __init__(self, ojos, cejas): #Definimos los Atributos self.ojos = ojos self.cejas = cejas class Madre(object): #Creamos la clase Padre def __init__(self, brazos, piernas): #Definimos los Atributos self.brazos = brazos self.piernas = piernas class Hijo(Padre, Madre): #Creamos clase hija que hereda de Padre y luego de Madre def __init__(self, ojos, cejas, cara, brazos, piernas): #creamos el constructor de la clase especificando atributos Madre.__init__(self, brazos, piernas) Padre.__init__(self, ojos, cejas)#Solicitamos a super llamar de la clase padre esos atributos self.cara = cara Tomas = Hijo('Marrones', 'Negras', 'Larga', 2, 2) print (Tomas.ojos, Tomas.cejas, Tomas.cara, Tomas.piernas, Tomas.brazos)
Resultado:
Marrones Negras Larga 2 2
Esto ha sido todo y espero hayas logrado comprender algo de esta función que nos va a ser muy útil en un futuro!!. Hasta luego! Bye bye! Ha y no olvides compartir, recomendarle a algún amigo interesado en python o suscribirte a mi blog! Muchas gracias por leerme 🙂
Entrada anterior! | Siguiente entrada! |
No entiendi nada de Super. Habria q hacerlo de cero este capitulo.
Saludos Fernando!
Lo que entendí del capitulo (Que me corrija el autor si no es así) es que la función “Super” nos servirá para heredar métodos de todas las clases superiores, siguiendo el orden en que fueron definidas las clases… Sin la necesidad de nombrar o especificar de que clase exactamente queremos heredar, si no, que la función buscará en los niveles superiores uno a uno hasta encontrar el primer resultado.
Me parece una función muy buena si pensamos en muchas clases y subclases una tras otra, derivando una de la anterior, finalmente, no tendríamos, que especificar cual de todas las clases queremos heredar un método, si no que lo haríamos con “Super”.
Espero te sirva mi comentario para aclararte.
PD: Muchas gracias al autor del post y la web por su contenido!!
Hola, primero decirte que tus clases son geniales. Antes para mi era todo un jeroglifico, ahora voy entiendiendo.
En cuanto a definir el modo __init__ en una clase secundaria, esto es necesario? no es lo mismo que herede la clase padre. Ahi no hace falta volver a definir __init__, por mas que se agregue un atributo. Para tu primer ejemplo lo hice asi, y da lo mismo.
class Padre(object): #Creamos la clase Padre
def __init__(self, ojos, cejas): #Definimos los Atributos en el constructor de la clase
self.ojos = ojos
self.cejas = cejas
class Hijo(Padre): #Creamos clase hija que hereda de Padre
def presentar (self, cara): #defino modo para imprimir, le doy un nuevo atributo.
self.cara = cara #Especificamos el nuevo atributo para Hijo
print(f”Tomas tiene ojos {self.ojos}, cejas {self.cejas}, y cara {self.cara}”)
Tomas = Hijo(‘Marrones’, ‘Negras’) #Instanciamos
Tomas.presentar(“larga”) #argumento para el atributo de accion presentar.
en una clase secundaria, cuando quiero agregar un atributo que no estaba en la clase primaria. es necesario definir el constructor __init__, para el atributo nuevo que estoy creando?. y tambien es necesario el padre.__init__(astributos de clase primaria?
Entendi en el otro tutorial anterior que al heredar de la clase primaria los atributos con su constructor __init__ ya no hacia falta poner de nuevo constructor.
otra consulta. porque en shepadoodle, que hereda de caniche y pastor aleman, hiciste el super?
y no en pastor aleman y en caniche que hereda de Perro?
es obligatorio el super para que funcione? o ya al heredar de una clase anterior se podria no poner?