La herencia es un mecanismo de la programación orientada a objetos que sirve para crear clases nuevas a partir de clases preexistentes. Se toman (heredan) atributos y comportamientos de las clases viejas y se los modifica para modelar una nueva situación.
La clase vieja se llama clase base y la que se construye a partir de ella es una clase derivada.
Por ejemplo, a partir de una clase Persona
(que contenga como atributos
identificacion
, nombre
, apellido
) podemos construir la clase
AlumnoFIUBA
que extiende a Persona
y agrega como atributo el padron
.
Para indicar el nombre de la clase base, se la pone entre paréntesis a
continuación del nombre de la clase (en lugar de la expresión object
que poníamos anteriormente; en realidad object
es el nombre de la clase
base genérica).
Definimos Persona
:
class Persona(object):
"Clase que representa una persona."
def __init__(self, identificacion, nombre, apellido):
"Constructor de Persona"
self.identificacion = identificacion
self.nombre = nombre
self.apellido = apellido
def __str__(self):
return " %s: %s, %s" %
(str(self.identificacion), self.apellido, self.nombre)
A continuación definimos AlumnoFIUBA
como derivada de Persona
, de forma
tal que inicialice el nuevo atributo, pero a su vez utilice la
inicialización de Persona
para las atributos de la clase base:
class AlumnoFIUBA(Persona):
"Clase que representa a un alumno de FIUBA."
def __init__(self, identificacion, nombre, apellido, padron):
"Constructor de AlumnoFIUBA"
# llamamos al constructor de Persona
Persona.__init__(self, identificacion, nombre, apellido)
# agregamos el nuevo atributo
self.padron = padron
Probamos la nueva clase:
>>> a = AlumnoFIUBA("DNI 35123456", "Damien", "Thorn", "98765")
>>> print a
DNI 35123456: Thorn, Damien
Vemos que se heredó el método __str__
de la clase base. Si queremos, podemos
redefinirlo:
def __str__(self):
"Devuelve una cadena representativa del alumno"
return " %d: %s, %s" %
(str(self.padron), self.apellido, self.nombre)
Volvemos a probar:
>>> a = AlumnoFIUBA("DNI 35123456", "Damien", "Thorn", "98765")
>>> print a
98765: Thorn, Damien
De una clase base se pueden construir muchas clases derivadas, así como hemos derivado alumnos, podríamos derivar docentes, empleados, clientes, proveedores, o lo que fuera necesario según la aplicación que estemos desarrollando.
Nota En el diseño de jerarquías de herencia no siempre es del todo fácil decidir cuándo una clase debe extender a otra. La regla práctica para decidir si una clase (S
) puede ser definida como heredera de otra (T
) es que debe cumplirse que "S es un T". Por ejemplo, Perro es un Animal, pero Vehículo no es un Motor.
Esta regla se desprende del principio de sustitución de Liskov (formulado por Barbara Liskov y Jeannette Wing).
Barbara Liskov es una mujer importante en la historia de la informática, no sólo por este principio, sino que fue la primera mujer en recibir un doctorado en las ciencias de la computación, creadora de varios lenguajes y actualmente es profesora e investigadora del MIT.
En el caso de Python, también se puede construir una clase derivada a partir de varias clases base (por ejemplo, un ayudante de segunda en la UBA es un alumno que también trabaja de docente). Esta posbilidad se llama Herencia Múltiple, pero no la detallaremos por ahora.
La clase de las figuras
Un ejemplo clásico de herencia es el de las figuras cerradas en el plano, con un método para calcular el área. En este caso, la clase base no tiene comportamiento definido ni atributos, dado que cada figura tiene atributos muy distintos (radio en el caso del círculo, base y altura en el caso del triángulo, etc.), y en cuanto al cálculo del área, cada figura tiene una fórmula diferente:
La clase base:
class Figura(object):
""" Una figura en el plano. """
def area(self):
" Este método debe ser redefinido. "
pass
Los círculos:
from math import pi
class Circulo(Figura):
""" Un círculo en el plano. """
def __init__(self, radio=0):
" Constructor de círculo. "
self.radio = radio
def area(self):
" Devuelve el área del círculo. "
return pi * self.radio * self.radio
Y los triángulos:
class Triangulo(Figura):
""" Un triángulo en el plano. """
def __init__(self, base=0, altura=0):
" Constructor de triángulo. "
self.base = base
self.altura = altura
def area(self):
" Devuelve el área del triángulo. "
return self.base * self.altura / 2.
Y ahora las pruebas:
>>> c = Circulo(4)
>>> c.area()
50.26548245743669
>>>
>>> t = Triangulo(3, 5)
>>> t.area()
7.5