Herencia
La herencia es una de las premisas y técnicas de la POO la cual permite a los programadores crear una clase general primero y luego más tarde crear clases más especializadas que re-utilicen código de la clase general. La herencia también le permite escribir un código más limpio y legible.
Clase Base
Clase Base o también conocida como Clase abstracta le permite definir una clase que puede heredarse en otras clases los atributos y comportamientos definido en esta.
Use el diagrama anterior para ilustrar el concepto de la herencia, vea el caso de dos clases que tiene algo en común, ambas son personas, con atributos de datos personales y comportamiento típicos como hablar, comer, caminar, entonces para eso se crea una clase base llamada Persona
. A continuación un ejemplo de la clase Persona
con un método interno:
class Persona(object):
"""Clase que representa una Persona"""
def __init__(self, cedula, nombre, apellido, sexo):
"""Constructor de clase Persona"""
self.cedula = cedula
self.nombre = nombre
self.apellido = apellido
self.sexo = sexo
def __str__(self):
"""Devuelve una cadena representativa de Persona"""
return "%s: %s, %s %s, %s." % (
self.__doc__[25:34], str(self.cedula), self.nombre,
self.apellido, self.getGenero(self.sexo))
def hablar(self, mensaje):
"""Mostrar mensaje de saludo de Persona"""
return mensaje
def getGenero(self, sexo):
"""Mostrar el genero de la Persona"""
genero = ('Masculino','Femenino')
if sexo == "M":
return genero[0]
elif sexo == "F":
return genero[1]
else:
return "Desconocido"
En el ejemplo previo, es donde empieza a crear una clase (lo hace con la palabra class
). La segunda palabra Persona
es el nombre de la clase. La tercera palabra que se encuentra dentro de los paréntesis este hace referencia al objeto object, usando para indicar la clase de la cual precede.
La clase Persona
tiene los métodos __init__
, __str__
, hablar
y getGenero
. Sus atributos son cedula
, nombre
, apellido
y sexo
.
La instancia de dos nuevos objetos Persona
seria de la siguiente forma:
persona1 = Persona("V-13458796", "Leonardo", "Caballero", "M")
persona2 = Persona("V-23569874", "Ana", "Poleo", "F")
El método constructor __init__
es un método especial el cual debe escribir como: MiClase(parámetros iniciales si hay cualquiera)
.
Usted puede llamar esos métodos y atributos con la siguiente notación: claseinstancia.metodo
o claseinstancia.atributo
.
>>> print persona1.nombre, persona1.apellido
>>> print persona1.getGenero(persona1.sexo)
El método __str__
es un método usando para imprimir la descripción de la instancia de objeto el cual debe mostrar como:
print "\n" + str(persona1) + "\n"
En el anterior código se usan para cierto formato para imprimir la instancia de objeto usando la sentencia print
, concatenando el carácter \n
para generar un salto de página y seguidamente convertir a formato cadena de caracteres usando la función str()
a la instancia de objeto llamada persona2
.
Herencia simple
La herencia simple se apoya en el uso de clases base para compartir sus atributos y comportamientos con otros clases derivadas como los siguiente ejemplos el objeto Supervisor
y el objeto Obrero
.
El siguiente es un ejemplo de la clase Supervisor
que derivada de la clase Persona
con función interna:
class Supervisor(Persona):
"""Clase que representa a un Supervisor"""
def __init__(self, cedula, nombre, apellido, sexo, rol):
"""Constructor de clase Supervisor"""
# Invoca al constructor de clase Persona
Persona.__init__(self, cedula, nombre, apellido, sexo)
# Nuevos atributos
self.rol = rol
self.tareas = ['10','11','12','13']
def __str__(self):
"""Devuelve una cadena representativa al Supervisor"""
return "%s: %s %s, rol: '%s', sus tareas: %s." % (
self.__doc__[26:37], self.nombre, self.apellido,
self.rol, self.consulta_tareas())
def consulta_tareas(self):
"""Mostrar las tareas del Supervisor"""
return ', '.join(self.tareas)
Ahora, se creará una nueva clase Supervisor
con los mismos métodos y atributos como la clase Persona
, pero con dos nuevos atributos rol
y tareas
. No se copia la clase previa, pero si se hereda de ella.
La instancia del nuevo objeto Supervisor
seria de la siguiente forma:
supervisor1 = Supervisor("V-16987456", "Jen", "Paz", "D", "Chivo")
Luego que generá la instancia del nuevo objeto Supervisor
llamada supervisor1
se puede imprimir sus detalles de la siguiente forma:
print "\n" + str(supervisor1) + "\n"
Como la instancia de objeto supervisor1
hereda los atributo(s) y método(s) de la clase Persona
usted puede reusarlo y llamarlo de la siguiente forma:
print "- Cedula de identidad: {0}.".format(supervisor1.cedula)
print "- Nombre completo: {0} {1}.".format(
supervisor1.nombre, supervisor1.apellido)
print "- Genero: {0}.".format(
supervisor1.getGenero(supervisor1.sexo))
print "- {0} {1} dijo: {2}".format(
supervisor1.nombre, supervisor1.apellido,
supervisor1.hablar("A trabajar Leonardo!!!".upper()))
Si desea usar los atributo(s) y método(s) heredados de la clase Supervisor
se puede imprimir de la siguiente forma:
print "- Rol: {0}.".format(supervisor1.rol)
print "- N. Tareas: {0}.".format(supervisor1.consulta_tareas())
El uso de las clases y la programación orientada a objetos, le permite a usted que pueda organizar el código con diferentes clases correspondientes a diferentes objetos que encontrará (una clase Persona
, una clase Carro
, una clase Departamento
, etc.), con sus propios métodos y atributos. Luego puede usar la herencia para considerar las variaciones en torno a una clase base y reutilizar el código. Ej.: a partir de una clase base de Persona
, usted puede crear clases derivadas como Supervisor
, JefeCuadrilla
, Obrero
, etc.
print """\nHola, Soy el {0} {1} {2}, mi cédula es '{3}',
mi genero '{4}', con el rol '{5}' y mis tareas
asignadas '{6}'.""".format(
supervisor1.__doc__[26:37].lower(),
supervisor1.nombre, supervisor1.apellido, supervisor1.cedula,
supervisor1.getGenero(supervisor1.sexo), supervisor1.rol,
supervisor1.consulta_tareas())
Función issubclass()
issubclass()
, es una función integrada la cual le permite corroborar si un objeto es instancia de una clase.
Herencia múltiple
A diferencia de lenguajes como Java y C#, el lenguaje Python permite la herencia múltiple, es decir, se puede heredar de múltiples clases.
La herencia múltiple es la capacidad de una subclase de heredar de múltiples súper clases.
Esto conlleva un problema, y es que si varias súper clases 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:
class Destreza(object):
"""Clase la cual representa la Destreza de la Persona"""
def __init__(self, area, herramienta, experiencia):
"""Constructor de clase Destreza"""
self.area = area
self.herramienta = herramienta
self.experiencia = experiencia
def __str__(self):
"""Devuelve una cadena representativa de la Destreza"""
return """Destreza en el área %s con la herramienta %s,
tiene %s años de experiencia.""" % (
str(self.area), self.experiencia, self.herramienta)
class JefeCuadrilla(Supervisor, Destreza):
"""Clase la cual representa al Jefe de Cuadrilla"""
def __init__(self, cedula, nombre, apellido, sexo,
rol, area, herramienta, experiencia, cuadrilla):
"""Constructor de clase Jefe de Cuadrilla"""
# Invoca al constructor de clase Supervisor
Supervisor.__init__(self, cedula, nombre, apellido, sexo,
rol)
# Invoca al constructor de clase Destreza
Destreza.__init__(self, area, herramienta, experiencia)
# Nuevos atributos
self.cuadrilla = cuadrilla
def __str__(self):
"""Devuelve cadena representativa al Jefe de Cuadrilla"""
jq = "{0}: {1} {2}, rol '{3}', tareas {4}, cuadrilla: {5}"
return jq.format(
self.__doc__[28:46], self.nombre, self.apellido,
self.rol, self.consulta_tareas(), self.cuadrilla)
Method Resolution Order (MRO)
Ese es el orden en el cual el método debe heredar en la presencia de herencia múltiple. Usted puede ver el MRO usando el atributo __mro__
.
>>> JefeCuadrilla.__mro__
(<class '__main__.JefeCuadrilla'>,
<class '__main__.Supervisor'>,
<class '__main__.Persona'>,
<class '__main__.Destreza'>,
<type 'object'>)
>>> Supervisor.__mro__
(<class '__main__.Supervisor'>,
<class '__main__.Persona'>,
<type 'object'>)
>>> Destreza.__mro__
(<class '__main__.Destreza'>,
<type 'object'>)
El MRO es calculado en Python de la siguiente forma:
Un método en la llamada derivada es siempre llamada antes de método de la clase base. En nuestro ejemplo, la clase JefeCuadrilla
es llamada antes de las clases Supervisor
o Destreza
. Esas dos clases son llamada antes de la clase Persona
y la clase Persona
es llamada antes de la clase object
.
Si hay herencia múltiple como JefeCuadrilla(Supervisor, Destreza)
, el método invoca a Supervisor
primero por que ese aparece primero de izquierda a derecha.