Sunteți pe pagina 1din 15

2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Herencia en la POO

Presentación
En esta unidad veremos en qué consiste la herencia y cómo aprovecharla para
ahorrarnos muchísimas líneas de código.

Herencia
La herencia es la capacidad que tiene una clase de heredar los atributos y
métodos de otra, algo que nos permite reutilizar código y hacer programar
mucho más óptimos.

Para ver su utilidad, en esta lección vamos a desarrollar un ejemplo. Partiremos


de una clase sin herencia con muchos atributos y la iremos descomponiendo en
otras clases más simples que nos permitan trabajar mejor con sus datos.

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 1/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Ejemplo sin herencia

Hace muchos años me vi en la necesidad de diseñar una estructura para una


tienda que vendía tres tipos de productos: adornos, alimentos y libros.

Todos los productos de la tienda tenían una serie de atributos comunes, como la
referencia, el nombre, el pvp... pero algunos eran especí cos de cada tipo.

Si partimos de una clase que contenga todos los atributos, quedaría más o
menos así:

Código Resultado

class Producto:
def __init__(self, referencia, tipo, nombre,
pvp, descripcion, productor=None,
distribuidor=None, isbn=None, autor=None):
self.referencia = referencia
self.tipo = tipo
self.nombre = nombre
self.pvp = pvp
self.descripcion = descripcion
self.productor = productor
self.distribuidor = distribuidor
self.isbn = isbn
self.autor = autor

adorno = Producto('000A','ADORNO','Vaso Adornado',15,


'Vaso de porcelana con dibujos')

print(adorno)
print(adorno.tipo)

Obviamente esto es un despropósito, así que veamos como aproecharnos de la


herencia para mejorar el planteamento.

Superclases

Así pues la idea de la herencia es identi car una clase base (la superclase) con
los atriutos comunes y luego crear las demás clases heredando de ella (las

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 2/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

subclases) extendiendo sus campos especí cos. En nuestro caso esa clase
sería el Producto en sí mismo:

class Producto:
def __init__(self,referencia,nombre,pvp,descripcion):
self.referencia = referencia
self.nombre = nombre
self.pvp = pvp
self.descripcion = descripcion

def __str__(self):
return f"REFERENCIA\t {self.referencia}\n" \
f"NOMBRE\t\t {self.nombre}\n" \
f"PVP\t\t {self.pvp}\n" \
f"DESCRIPCIÓN\t {self.descripcion}\n"

Subclases

Para heredar los atributos y métodos de una clase en otra sólo tenemos que
pasarla entre paréntesis durante la de nición:

Código Resultado

class Adorno(Producto):
pass

adorno = Adorno(2034, "Vaso adornado", 15, "Vaso de porcelana")


print(adorno)

Como se puede apreciar es posible utilizar el comportamiento de una


superclase sin de nir nada en la subclase.

Respecto a las demás subclases como se añaden algunos atributos, podríamos


de nirlas de esta forma:

class Alimento(Producto):
productor = ""
distribuidor = ""

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 3/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

def __str__(self):
return f"REFERENCIA\t {self.referencia}\n" \
f"NOMBRE\t\t {self.nombre}\n" \
f"PVP\t\t {self.pvp}\n" \
f"DESCRIPCIÓN\t {self.descripcion}\n" \
f"PRODUCTOR\t\t {self.productor}\n" \
f"DISTRIBUIDOR\t\t {self.distribuidor}\n"

class Libro(Producto):
isbn = ""
autor = ""

def __str__(self):
return f"REFERENCIA\t {self.referencia}\n" \
f"NOMBRE\t\t {self.nombre}\n" \
f"PVP\t\t {self.pvp}\n" \
f"DESCRIPCIÓN\t {self.descripcion}\n" \
f"ISBN\t\t {self.isbn}\n" \
f"AUTOR\t\t {self.autor}\n"

Ahora para utilizarlas simplemente tendríamos que establecer los atributos


después de crear los objetos:

Código Resultado

alimento = Alimento(2035, "Botella de Aceite de Oliva", 5, "250


ML")
alimento.productor = "La Aceitera"
alimento.distribuidor = "Distribuciones SA"
print(alimento)

libro = Libro(2036, "Cocina Mediterránea",9, "Recetas sanas y


buenas")
libro.isbn = "0-123456-78-9"
libro.autor = "Doña Juana"
print(libro)

Luego en los ejercicios os mostraré como podemos sobreescribir el constructor


de una forma e ciente para inicializar campos extra, por ahora veamos como
trabajar con estos objetos de distintas clases de forma común.

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 4/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Trabajando en conjunto
Gracias a la exibilidad de Python podemos manejar objetos de distintas clases
masivamente de una forma muy simple.

Vamos a empezar creando una lista con nuestros tres productos de subclases
distintas:

Código Resultado

productos = [adorno, alimento]


productos.append(libro)

print(productos)

Ahora si queremos recorrer todos los productos de la lista podemos usar un


bucle for:

Código Resultado

for producto in productos:


print(producto, "\n")

También podemos acceder a los atributos, siempre que sean compartidos entre
todos los objetos:

Código Resultado

for producto in productos:


print(producto.referencia, producto.nombre)

Si un objeto no tiene el atributo al que queremos acceder nos dará error:

Código Resultado

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 5/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

for producto in productos:


print(producto.autor)

Por suerte podemos hacer una comprobación con la función isinstance() para
determinar si una instancia es de una determinado clase y así mostrar unos
atributos u otros:

Código Resultado

for producto in productos:


if( isinstance(producto, Adorno) ):
print(producto.referencia, producto.nombre)
elif( isinstance(producto, Alimento) ):
print(producto.referencia, producto.nombre,
producto.productor)
elif( isinstance(producto, Libro) ):
print(producto.referencia, producto.nombre, p.isbn)

Polimor smo
El polimor smo es una propiedad de la herencia por la que objetos de distintas
subclases pueden responder a una misma acción.

La polimor a es implícita en Python, ya que todas las clases son subclases de


una superclase común llamada Object.

Por ejemplo la siguiente función aplica una rebaja al precio de un producto:

def rebajar_producto(producto, rebaja):


producto.pvp = producto.pvp - (producto.pvp/100 * rebaja)

Gracias al polimor smo no tenemos que comprobar si un objeto tiene o no el


atributo pvp, simplemente intentamos acceder y si existe premio:

Código Resultado

print(alimento, "\n")

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 6/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

rebajar_producto(alimento, 10)
print(alimento)

Por cierto, como podéis ver en el ejemplo, cuando modi camos un atributo de
un objeto dentro de una función éste cambia en la instancia. Esto es por aquello
que os comenté del paso por valor y referencia.

Copia de objetos
De la misma forma que las colecciones, los objetos se pasan a las funciones por
referencia. Si modi camos sus valores dentro, éstos se verán re ejados fuera.

Esto también afecta a la hora de hacer copias, creándose en su lugar un acceso


al objeto en lugar de uno nuevo con sus valores:

Código Resultado

class Test:
pass

test1 = Test()
test2 = test1

test1.algo = "Prueba"

print(test2 == test1) # ¿Son el mismo objeto?

try:
print(test2.algo)
except Exception as e:
print(e)

Para realizar una copia a partir de sus valores podemos utilizar la función copy
del módulo con el mismo nombre:

Código Resultado

from copy import copy

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 7/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

class Test:
pass

test1 = Test()
test2 = copy(test1)

test1.algo = "Prueba"

print(test2 == test1) # ¿Son el mismo objeto?

try:
print(test2.algo)
except Exception as e:
print(e)

La función copy se puede utilizar también para copiar colecciones:

Código Resultado

from copy import copy

lista1 = [1,2,3]
lista2 = copy(lista1)
lista1 = None

print(lista1)
print(lista2)

Herencia múltiple
Finalmente hablemos de la herencia múltiples, la capacidad de que una subclase
de heredar de múltiples superclases.

Esto conlleva un problema, y es que si varias superclases tienen los mismos


atributos o métodos, la subclase sólo podrá heredar de una de ellas.

En estos casos Python dará prioridad a las clases más a la izquierda en el


momento de la declaración de la subclase:

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 8/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Código Resultado

class A:
def __init__(self):
print("Soy de clase A")
def a(self):
print("Este método lo heredo de A")

class B:
def __init__(self):
print("Soy de clase B")
def b(self):
print("Este método lo heredo de B")

class C(B,A):
def c(self):
print("Este método es de C")

c = C()
c.a()
c.b()
c.c()

Ejercicio optativo
Teoría previa

En este ejercicio vas a trabajar el concepto de herencia un poco más en


profundidad, aprovechando para introducir un nuevo concepto muy importante
que te facilitará la vida.

Hasta ahora sabemos que una clase heredada puede fácilmente extender
algunas funcionalidades, simplemente añadiendo nuevos atributos y métodos, o
sobreescribiendo los ya existentes. Como en el siguiente ejemplo:

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 9/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Ejercicio Resultado

class Vehiculo():

def __init__(self, color, ruedas):


self.color = color
self.ruedas = ruedas

def __str__(self):
return "Color {}, {} ruedas".format( self.color,
self.ruedas )

class Coche(Vehiculo):

def __init__(self, color, ruedas, velocidad, cilindrada):


self.color = color
self.ruedas = ruedas
self.velocidad = velocidad
self.cilindrada = cilindrada

def __str__(self):
return "color {}, {} km/h, {} ruedas, {} cc".format(
self.color, self.velocidad, self.ruedas, self.cilindrada )

coche = Coche("azul", 150, 4, 1200)


print(coche)

El inconveniente más evidente de ir sobreescribiendo es que tenemos que volver


a escribir el código de la superclase y luego el especí co de la subclase.

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 10/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Para evitarnos escribir código innecesario, podemos utilizar un truco que


consiste en llamar el método de la superclase y luego simplemente escribir el
código de la clase:

Ejercicio Resultado

class Vehiculo():

def __init__(self, color, ruedas):


self.color = color
self.ruedas = ruedas

def __str__(self):
return "Color {}, {} ruedas".format( self.color,
self.ruedas )

class Coche(Vehiculo):

def __init__(self, color, ruedas, velocidad, cilindrada):


Vehiculo.__init__(self, color, ruedas)
self.velocidad = velocidad
self.cilindrada = cilindrada

def __str__(self):
return Vehiculo.__str__(self) + ", {} km/h, {}
cc".format(self.velocidad, self.cilindrada)

c = Coche("azul", 4, 150, 1200)


print(c)

Como tener que determinar constantemente la superclase puede ser fastidioso,


Python nos permite utilizar un acceso directo mucho más cómodo llamado
super().

Hacerlo de esta forma además nos permite llamar cómodamente los métodos o
atributos de la superclase sin necesidad de especi car el self, pero ojo, sólo se
aconseja utilizarlo cuando tenemos una única superclase:

Ejercicio Resultado

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 11/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

class Vehiculo():

def __init__(self, color, ruedas):


self.color = color
self.ruedas = ruedas

def __str__(self):
return "color {}, {} ruedas".format( self.color,
self.ruedas )

class Coche(Vehiculo):

def __init__(self, color, ruedas, velocidad, cilindrada):


Vehiculo.__init__(self, color, ruedas)
self.velocidad = velocidad
self.cilindrada = cilindrada

def __str__(self):
return Vehiculo.__str__(self) + ", {} km/h, {}
cc".format(self.velocidad, self.cilindrada)

c = Coche("azul", 4, 150, 1200)


print(c)

Enunciado

Utilizando esta nueva técnica extiende la clase Vehiculo y realiza la siguiente


implementación:

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 12/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Crea al menos un objeto de cada subclase y añádelos a una lista llamada


vehiculos.

Realiza una función llamada catalogar() que reciba la lista de vehiculos y los
recorra mostrando el nombre de su clase y sus atributos.

Modi ca la función catalogar() para que reciba un argumento optativo


ruedas, haciendo que muestre únicamente los que su número de ruedas
concuerde con el valor del argumento. También debe mostrar un mensaje
"Se han encontrado {} vehículos con {} ruedas:" únicamente si se envía el
argumento ruedas. Ponla a prueba con 0, 2 y 4 ruedas como valor.

 Recordatorio

Puedes utilizar el atributo especial de clase name para recuperar el nombre de la clase de un
objeto:

type(objeto).__name__

Solución

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 13/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Ejercicio Solución Resultado

class Vehiculo():

def __init__(self, color, ruedas):


self.color = color
self.ruedas = ruedas

def __str__(self):
return "color {}, {} ruedas".format( self.color,
self.ruedas )

class Coche(Vehiculo):

def __init__(self, color, ruedas, velocidad, cilindrada):


super().__init__(color, ruedas) # utilizamos super()
self.velocidad = velocidad
self.cilindrada = cilindrada

def __str__(self):
return super().__str__() + ", {} km/h, {} cc".format(
self.velocidad, self.cilindrada)

# Completa el ejercicio aquí

class Camioneta():
pass

class Bicicleta():
pass

class Motocicleta():
pass

def catalogar():
pass

def catalogar():
pass

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 14/15
2/10/2018 Herencia en la POO | Curso de Python | Hektor Profe

Última edición: 29 de Septiembre de 2018

https://www.hektorprofe.net/python/herencia-en-la-poo/#herencia 15/15

S-ar putea să vă placă și