Polimorfismo
A veces aplicamos los mismos métodos o funciones sobre objetos de distintas clases o tipos. Por ejemplo,
Llamamos interfaz al conjunto de atributos y métodos que un programador desarrolla para una clase. Cuando se cumple el polimorfismo, existe una interfaz común aplicable a objetos de distintas clases.
Se llama redefinición a la acción de definir un método con idéntico nombre para varias clases, de manera que provea de una interfaz común. En cada clase el método debe programarse de una forma distinta. Por ejemplo, los métodos especiales que permiten la sobrecarga de operadores vista anteriormente.
- Podemos aplicar la iteración for i in secuencia a un objeto secuencia que puede ser una lista, una tupla, un diccionario, etc.
- O podemos aplicar la suma, la resta, u otras funciones a números enteros, tipo float, tipo long...
- O por ejemplo, al incluir una clase nueva aplicamos el método __str__ para formatear la salida del mismo.
Llamamos interfaz al conjunto de atributos y métodos que un programador desarrolla para una clase. Cuando se cumple el polimorfismo, existe una interfaz común aplicable a objetos de distintas clases.
Se llama redefinición a la acción de definir un método con idéntico nombre para varias clases, de manera que provea de una interfaz común. En cada clase el método debe programarse de una forma distinta. Por ejemplo, los métodos especiales que permiten la sobrecarga de operadores vista anteriormente.
Herencia
La herencia
consiste que podemos generar una clase basada en una clase anterior,
heredando sus métodos y atributos. En el ejemplo siguiente, generamos la
clase vehículo que contiene la inicialización de diversas características, como color, tipo de combustible, cilindrada y capacidad del tanque, y dos métodos, andar, que gasta 2 litros de combustible cada vez y repostar, que llena el tanque. Será nuestra clase base (entre paréntesis en la definición object).
A continuación generamos la clase coche, que hereda de vehículo todos sus atributos y añade el de ruedas. Se define el método de formateo o de muestra de información. Esta es una clase derivada y en su definición se pone entre paréntesis de qué clase deriva class coche(vehiculo). Asímismo, genero la clase moto, de la misma forma.
En el programa, los métodos aplicados a la clase moto son repostar y andar. Ambos pueden aplicarse porque la clase moto ha heredado
de la clase vehículo sus métodos. También, al imprimir o mostrar sus
características se muestran los atributos heredados de la clase
vehículo.
Una clase puede heredarse de otra cuando cumple el principio de Liskov:
- La clase A es heredera de otra B cuando estoy seguro que "A es B".
- La clase coche o moto son herederas de vehículo porque estoy seguro que "un coche o una moto son vehículos".
- Ahora, la clase motor no puede heredarse de la clase vehículo porque "un motor no es un vehículo"
# -*- coding: utf-8 -*- class vehiculo(object): def __init__(self, c0="rojo", comb0="diesel", cil=200, tan=80): self.color = c0 self.combustible = comb0 self.cilindrada = cil #capacidad del tanque de combustible self.tanque = tan self.maxTanque = tan return def andar(self): self.tanque -= 2 return def repostar(self): self.tanque = self.maxTanque return class coche(vehiculo): def __init__(self, c0="rojo", comb0="diesel", cil=200, tan=80, rued = 4): # llamo a la clase anterior vehiculo vehiculo.__init__(self,c0,comb0,cil,tan) # añado nueva característica self.ruedas = rued return def __str__(self): car = "Vehículo de " + str(self.ruedas) + " ruedas, " + self.combustible car += ", color " + self.color + " y cilindrada " + str(self.cilindrada)+"." car += "\nLe quedan en el tanque "+ str(self.tanque) + " litros." return car class moto(vehiculo): def __init__(self, c0="rojo", comb0="diesel", cil=200, tan=80, rued = 2): # llamo a la clase anterior vehiculo vehiculo.__init__(self,c0,comb0,cil,tan) # añado nueva característica self.ruedas = rued return def __str__(self): car = "Vehículo de " + str(self.ruedas) + " ruedas, " + self.combustible car += ", color " + self.color + " y cilindrada " + str(self.cilindrada)+"." car += "\nLe quedan en el tanque "+ str(self.tanque) + " litros." return car # ========================================= # Programa Principal # ========================================= # crear dos vehículos distintos, con consideraciones iniciales c1 = coche() m1 = moto() # Imprime información de los dos vehículos print c1 print m1 # Crea un vehículo usando herencia para definir parte de sus caraterísticas m2 = moto( "azul", "gasolina", 400, 200, 2) # lo imprime print m2 print "\n" # Hago andar el vehículo 2, 10 veces. for i in range(10): m2.andar() print "He movido la moto 10 veces:\n", m2 # Le lleno el tanque m2.repostar() print "Le he llenado el tanque:\n", m2
= = =
Delegación
Hay
clases que no pueden heredarse de otra ya que no cumplen el principio
de Liskov, pero sí tienen cierta relación. Antes comentamos que un motor
no es un vehículo, luego no puede haber herencia entre ellos, pero evidentemente un vehículo posee un motor.
Pero nada impide que la clase vehículo tenga como atributo un objeto de otra clase, llamada motor. En este caso, la clase vehículo delega en la clase motor parte de sus funcionalidades. Decimos que el atributo motor tiene una referencia en la clase motor.
En el siguiente ejemplo, incluyo la
clase motor. La clase vehículo recibe el atributo motor como una
instancia de dicha clase, que se hereda en las clases coche y moto.
# -*- coding: utf-8 -*- class vehiculo(object): def __init__(self, c0="rojo", comb0="diesel", cil=200, tan=80, mt=("",0)): self.color = c0 self.combustible = comb0 self.cilindrada = cil #capacidad del tanque de combustible self.tanque = tan self.maxTanque = tan self.motor = mt return def andar(self): self.tanque -= 2 return def repostar(self): self.tanque = self.maxTanque return class motor(object): def __init__(self, marca="Mercedes", cilindros=4): self.marca = marca self.cilindros = cilindros return def __str__(self): return "["+self.marca + ":" + str(self.cilindros)+ "]" def gripar(self): self.marca="Estropeado" self.cilindros=0 return class coche(vehiculo): def __init__(self, c0="rojo", comb0="diesel", cil=200, tan=80, rued = 4, mt=("",0) ): # llamo a la clase anterior vehiculo vehiculo.__init__(self,c0,comb0,cil,tan, mt) # añado nueva característica self.ruedas = rued return def __str__(self): car = "Vehículo de " + str(self.ruedas) + " ruedas, " + self.combustible car += ", color " + self.color + " y cilindrada " + str(self.cilindrada)+"." car += "\nDe motor: " + str(self.motor) + "." car += "\nLe quedan en el tanque "+ str(self.tanque) + " litros." return car class moto(vehiculo): def __init__(self, c0="rojo", comb0="diesel", cil=200, tan=80, rued = 2, mt=("",0) ): # llamo a la clase anterior vehiculo vehiculo.__init__(self,c0,comb0,cil,tan,mt) # añado nueva característica self.ruedas = rued return def __str__(self): car = "Vehículo de " + str(self.ruedas) + " ruedas, " + self.combustible car += ", color " + self.color + " y cilindrada " + str(self.cilindrada)+"." car += "\nDe motor: " + str(self.motor) + "." car += "\nLe quedan en el tanque "+ str(self.tanque) + " litros." return car # ========================================= # Programa Principal # ========================================= # Dos motores mt1 = motor("RENAULT",12) mt2 = motor("HONDA",6) # crear dos vehículos distintos, con consideraciones iniciales c1 = coche("negro","gasolina",500,120,4,mt1) m1 = moto("amarilla","diesel",900,180,2,mt2) # Imprime información de los dos vehículos print c1 print m1 # Crea un vehículo usando herencia para definir parte de sus caraterísticas m2 = moto( "azul", "gasolina", 400, 200, 2,mt2) # lo imprime print m2 print "\n" # Hago andar el vehículo 2, 10 veces. for i in range(10): m2.andar() print "He movido la moto 10 veces:\n", m2, "\n" # Le lleno el tanque m2.repostar() print "Le he llenado el tanque:\n", m2, "\n" # Gripo el motor de la segunda moto m2.motor.gripar() print "¿Qué le ha pasado al motor de la moto?: ",m2.motor
= = =