31 dic. 2016

Volviendo a lo básico, POO en Python ( composición) (parte 9)

Volviendo a lo básico, POO en Python ( composición) (parte 9)

Para terminar la serie de artículos sobre programación orientada a objetos con python

La composición significa utilizar objetos dentro de otro objetos sin usar herencia.

A continuación se muestra el diagrama UML de dos objetos A y B. 



A continuación se muestra el código de ejemplo: 

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Composición"""
class A(object):
    def a1(self):
        print("a1")
class B(object):
    def b(self):
        print ("b")
        A().a1()
if __name__ =="__main__":
    objetoB = B()
    objetoB.b()

Al ejecutar el código se tiene la siguiente salida:
b
a1


Este ejemplo es algo sencillo. La composición es otra forma de reutilizar código.

El código lo pueden ver en gitlab



Volviendo a lo básico, POO en Python ( herencia multiple, problema del diamante) (parte 8)

Volviendo a lo básico, POO en Python ( herencia multiple, problema del diamante) (parte 8)


Continuando con los artículos sobre programación orientada a objetos en python.

En el artículo anterior se explicó como trabajar con la herencia multiple. Ahora se  explicará el problema del diamante y como lo resuelve Python.

La herencia en diamante se muestra en la siguiente figura:

Donde la clase padre es la clase A, y la clase B y C heredan de ella, al final la clase D hereda tanto de B como de C y ambas clases como ya se saben heredan de A. ¿Por que vía se hereda de A, por B o por C?

Ese es en sí el problema del diamante; pueden encontrar más información en la página de wikipedia

Cada lenguaje tiene una forma de resolver este problema, para el caso de Python, este crea una lista de clases, que se buscan de izquierda a derecha y de abajo a arriba (D,B,A,C,A), luego elimina todas las apariciones de una clase repetida menos la última. Por lo que el orden de resolución queda D,B,C,A. 

A continuación se muestra un ejemplo en python: 


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""multiple herencia, problema del diamante"""
class A(object):
    def __init__(self,mensajeA):
        self.mensajeA = mensajeA
    @staticmethod
    def quienSoy():
        return "Soy A"
class B(A):
    def __init__(self,mensajeA, mensajeB):
        A.__init__(self,mensajeA)
        self.mensajeB = mensajeB
    @staticmethod
    def quienSoy():
        return "Soy B"
class C(A):
    def __init__(self,mensajeA,mensajeC):
        A.__init__(self,mensajeA)
        self.mensajeC = mensajeC
    @staticmethod
    def quienSoy():
        return "Soy C"
class D(B,C):
    def __init__(self,mensajeA,mensajeB,mensajeC,mensajeD):
        B.__init__(self,mensajeA,mensajeB)
        C.__init__(self,mensajeA,mensajeC)
        self.mensajeD = mensajeD
    @staticmethod
    def quienSoy():
        return "Soy D"
if __name__ == '__main__':
    ca = A("prueba de A")
    cb = B("prueba de A desde B","prueba de B")
    cc = C("prueba de A desde C","prueba de C")
    cd = D("prueba de A desde D","prueba de B desde D","prueba de C desde D","prueba de D")
    print ("Mensaje de A:",ca.mensajeA)
    print ("Quien es?: ",ca.quienSoy())
    print ("Mensaje 1 de B:", cb.mensajeB)
    print("Mensaje 2 de B:", cb.mensajeA)
    print ("Quien es?:",cb.quienSoy())
    print ("Mensaje 1 de C:", cc.mensajeC)
    print ("Mensaje 2 de C:", cc.mensajeA)
    print ("Quien es?:", cc.quienSoy())
    print ("Mensaje 1 de D:", cd.mensajeD)
    print ("Mensaje 2 de D:", cd.mensajeB)
    print ("Mensaje 3 de D:", cd.mensajeC)
    print ("Mensaje 4 de D:", cd.mensajeA)
    print("Quien es?:", cd.quienSoy())

Noten que no se usó super, ya que para poder resolver la evaluación se hace más fácil definiendo el objeto directamente en vez de usar super una llamada de la herencia. 


Al ejecutar el código se tiene la siguiente salida:

Mensaje de A: prueba de A
Quien es?:  Soy A
Mensaje 1 de B: prueba de B
Mensaje 2 de B: prueba de A desde B
Quien es?: Soy B
Mensaje 1 de C: prueba de C
Mensaje 2 de C: prueba de A desde C
Quien es?: Soy C
Mensaje 1 de D: prueba de D
Mensaje 2 de D: prueba de B desde D
Mensaje 3 de D: prueba de C desde D
Mensaje 4 de D: prueba de A desde D
Quien es?: Soy D


Como pueden notar la ejecución de la última clase muestra la secuencia explicada en este artículo.

El código de este ejemplo lo pueden descargar desde gitlab





Volviendo a lo básico, POO en Python ( Multipleherencia) (parte 7)

Volviendo a lo básico, POO en Python (multiple herencia) (parte 7)


Continuando con los artículos de programación orientada a objetos con python, en este caso se tocará el caso de multiple herencia.

La siguiente figura se muestra el diagrama UML:


Se tiene las siguientes clases y de quien hereda:
  • Direccion(object): Tiene un método init que recibe los  argumentos calle, ciudad, estado y codigo.
  • Contacto(object): Tiene un método init con recibe los  argumentos nombre y correo.
  • Amigo(Contacto,Direccion): Tiene un método init que recibe los argumentos nombre, correo, telefono, calle, ciudad, estado y codigo.
  • ListaContactos: Tiene un método buscar que recibe un nombre como argumento.
Clase ListaContactos:

class ListaContactos(list):
    def buscar(self,nombre):
        '''Retorna todos los contactos que contengan el nombre'''
        contactos_encontrados = []
        for contacto in self:
            if nombre in contacto.nombre:
                contactos_encontrados.append(nombre)
        return contactos_encontrados


Clase Direccion:

class Direccion(object):
    def __init__(self,calle, ciudad,estado, codigo):
        '''Inicializa la clase con la calle, ciudad, estado y codigo postal)'''
        self.calle = calle
        self.ciudad = ciudad
        self.estado = estado
        self.codigo = codigo



Clase Contacto:
class Contacto(object):
    '''Clase contacto que guarda una lista de los contactos e inicializa con el nombre y el correo del contacto'''
    todos_contactos  = ListaContactos()
    def __init__(self,nombre, correo):
        self.nombre = nombre
        self.correo = correo
        Contacto.todos_contactos.append(self)



Clase Amigo:
class Amigo(Contacto,Direccion):
    def __init__(self,nombre, correo, telefono,calle,ciudad,estado,codigo):
        Contacto.__init__(self,nombre,correo)
        self.telefono = telefono
        Direccion.__init__(self,calle,ciudad,estado,codigo)


La instanciación de la clase amigo se muestra a continuación: 

contacto = Amigo("Ernesto","seraph2@contacto.com","04155556565","paez","guacara","carabobo","2015")
print(contacto.nombre,contacto.correo,contacto.telefono,contacto.calle,contacto.ciudad,contacto.estado,contacto.codigo)


Al ejecutar el programa se tiene la siguiente salida:

Ernesto seraph2@contacto.com 04155556565 paez guacara carabobo 2015


El código completo de este artículo se encuentra en gitlab en el siguiente enlace.


Volviendo a lo básico, POO en Python (parte 6)

Volviendo a lo básico, POO en Python (parte 6)


Continuando con la serie de artículos sobre Programación Orientada a Objetos en Python.

En este artículo se explica el uso del decorador classmethod el cual se le pasa como argumentos la clase. 

Se tiene una clase abstracta que se llama figura, en ella  se tiene los siguientes decoradores:
  • abstractmethod: Métodos abstractos que no contienen nada, sólo la descripción de los métodos, estos métodos son: area, perimetro y  quienEresTu.
  • abstractproperty: Propiedad abstracta, en este caso se define el método color. 
  • property: El método color devuelve el color como propiedad.
  • @color.setter: Define un nuevo color. 
  • classmethod: Se define __subclasshook__ como un classmethod, quien permite personalizar el comportamiento de Issubclass sin necesidad de registrar subclase de la clase abstracta (este método debe devolver True, False o NotImplemented).

Luego se crea la clase Rectangulo que hereda de la clase abstracta Figura, luego se crea la clase Cuadrado que hereda de la clase Rectangulo.


A continuación se muestra el código de la clase Figura:



#Se importa sqrt de math
from math import sqrt
#Se importa ABCMeta, abstractmethod, abstractproperty
from abc import ABCMeta, abstractmethod,abstractproperty
#Interface
class Figura(object):
    __metaclass__ = ABCMeta
    @abstractmethod
    def area(self):
        """Calcula el area"""
        pass
    @abstractmethod
    def perimetro(self):
        """Calcula el perimetro"""
        pass
    @abstractmethod
    def quienEresTu(self):
        """Devuelve el mensaje de quien es el objeto"""
        print (u"Soy una forma y un método abstracto ")
    @abstractproperty
    def color(self):
        pass
    @property
    def color(self):
        pass
    @color.setter
    def color(self,valor):
        pass
    @classmethod
    def __subclasshook__(cls, C):
        return NotImplemented


Se muestra el código de la clase Rectangulo:


class Rectangulo(Figura):     def __init__(self,ancho, alto):         """Define los atributos de rectangulo"""         self.ancho, self.alto = ancho, alto         super(Rectangulo,self).__init__()         self.__color = "rojo"     def area(self):         """Devuelve el area"""         return self.ancho*self.alto     def perimetro(self):         """devuelve el perimetro"""         return 2*self.ancho+2*self.alto     @property     def color(self):         """Devuelve el atributo color"""         return self.__color     @color.setter     def color(self,valor):         """Define el color del rectangulo"""         self.__color = valor     def quienEresTu(self):         """devuelve que es el objeto"""         print (u"Soy un Rectangulo")


A continuación se muestra el código de la clase Cuadrado:

class Cuadrado(Rectangulo):

    def __init__(self,largo):
        """Define el largo del cuadrado y se lo pasa a la clase rectangulo"""
        self.largo = largo
        super(Cuadrado,self).__init__(largo,largo)

    def area(self):
        """DEvuelve el area del cuadrado"""
        return self.largo*self.largo


Para terminar se muestra la implementación de las clases:
if __name__ == '__main__':
    r = Rectangulo(5,6)
    print ("Es una subclase rectangulo de shape?:",issubclass(Rectangulo,Figura))
    print ("Es una subclase cuadrado de shape?:",issubclass(Cuadrado,Figura))
    print ("Es una subclase cuadrado de rectangulo?:",issubclass(Cuadrado,Rectangulo))
    print ("Es una subclase rectangulo de cuadrado?:",issubclass(Rectangulo,Cuadrado))
    print ("r es una instancia de rectangulo?:",isinstance(r,Rectangulo))
    print ("r es una instancia de cuadrado?:",isinstance(r,Cuadrado))
    print ("Area del rectangulo:",r.area())
    print("Color del rectangulo:",r.color)
    r.color = "Black"
    print("Color del rectangulo:",r.color)
    print ("Perimetro del rectangulo",r.perimetro())
    r.quienEresTu()
    sq = Cuadrado(5)
    print ("Area del cuadrado:",sq.area())
    print ("Perimetro del cuadrado:",sq.perimetro())



Al ejecutar el código se tiene el siguiente resultado:


Es una subclase rectangulo de shape?: True
Es una subclase cuadrado de shape?: True
Es una subclase cuadrado de rectangulo?: True
Es una subclase rectangulo de cuadrado?: False
r es una instancia de rectangulo?: True
r es una instancia de cuadrado?: False
Area del rectangulo: 30
Color del rectangulo: rojo
Color del rectangulo: Black
Perimetro del rectangulo 22
Soy un Rectangulo
Area del cuadrado: 25
Perimetro del cuadrado: 20


El código completo de las clases lo pueden encontrar en gitlab en el siguiente enlace.



19 dic. 2016

Volviendo a lo básico, POO en Python (parte 5)

Volviendo a lo básico, POO en Python (parte 5)


Continuando con los artículos sobre Programación Orientada a Objetos en Python, en este caso se usará lo que se vió en la parte 4 de la serie, donde se creó una clase Punto2D y se creó una clase hija Punto3D que heredaba de la Punto2D.

En este caso se incorporarán nuevos conceptos:
  • meta clases (clase abstracta)
  • método abstracto: 
Con la clase abstracta se define una clase con sus métodos abstractos que no hacen nada, sólo declaran las clases, luego se hereda dicha clase y se van implementando los métodos que se definieron abstractactos. 

En este caso se crea la clase abstracta ADTPunto que define una serie de métodos, luego se crea la clase punto que hereda de ADTPunto, aquí se implementan los métodos que se definieron en la clase abstracta, la clase abstacta termina siendo como una interfaz de una clase. 

A continuación se muestra el código de la clase abstracta punto: 

from abc import ABCMeta, abstractmethod
class ADTPunto (object):
    __metaclass__ = ABCMeta
    @abstractmethod
    def ValorX(self):
        pass
    @abstractmethod
    def ValorY(self):
        pass
    @abstractmethod
    def mover(self,puntoNuevo):
        pass
    @abstractmethod
    def reset(self):
        pass
    @abstractmethod
    def distanciaOtroPunto(self,punto):
        pass

A continuación se muestra la clase Punto que hereda de ADTPunto:
from math import sqrt
class Punto(ADTPunto):
    def __init__(self,x,y):
        self.__x = x
        self.__y = y
        super(Punto,self).__init__()
    @property
    def punto(self):
        """el getter de punto, devuelve el punto"""
        return (self.__x,self.__y)
    @punto.setter
    def punto(self,Punto):
        """Asigna nuevo valor al punto"""
        self.__x = Punto.punto[0]
        self.__y = Punto.punto[1]
    @punto.deleter
    def punto(self):
        """Borra los valores del punto"""
        del self.__x
        del self.__y
    @property
    def ValorX(self):
        """Devuelve el valor de x"""
        return self.__x
    @property
    def ValorY(self):
        """Devuelve el valor de y"""
        return self.__y
    def reset(self):
        """Fija el punto en (0,0)"""
        self.__x = 0
        self.__y = 0
    def distanciaOtroPunto(self,oPunto):
        '''devuelve la distancia entre el punto original y un punto dado'''
        return sqrt((self.__x - oPunto.punto[0])**2 + (self.__y - oPunto.punto[1])**2 )
    def mover(self,oPunto):
        """Cambia el punto a un nuevo punto"""
        self.__x = oPunto.punto[0]
        self.__y = oPunto.punto[1]

Para finalizar se muestra la instanciación de ambas clases:

if __name__ == "__main__":
    try:
        prueba = ADTPunto()
    except (TypeError):
        print ("No puede instanciar una clase abstracta")
    punto2d = Punto(3,5)
    print(punto2d.punto)
    print(punto2d.ValorX)
    print(punto2d.ValorY)
    punto2d.mover(Punto(5,5))
    print(punto2d.punto)
    print(punto2d.distanciaOtroPunto(Punto(10,10)))
    punto2d.reset()
    print(punto2d.punto)

Al ejecutar el script se tiene lo siguiente:

python ej12_adt.py 
No puede instanciar una clase abstracta
(3, 5)
3
5
(5, 5)
7.07106781187
(0, 0)


Como se puede ver, se intento instanciar la clase ADTPunto, lo cual genera un error de tipo.

El código completo de las clases se encuentran en gitlab en el repo tutorial-poo.