24 feb. 2017

De vuelta a lo básico, como manejar colas en python.

Las estructuras de datos tipo cola son tipo FIFO (primero que entra, primero que sale).

El artículo se basa de la documentación oficial de Python en inglés.
Se puede usar las listas como una cola.

Las listas soportan las siguientes métodos:

  • list.append(x): agrega un elemento al final de la lista, equivalente a a[len(a):] =[x].
  • list.extend(L): extiende una lista al agregar todos los elementos de una lista, equivalente a a[len(a):] = L.
  • list.insert(i,x): Inserta un elemento en la lista en una posición dada. 
  • list.remove(x): Elimina el primer item x de la lista.
  • list.pop([i]):Elimina el item en la posición dada de la lista, y lo devuelve.
  • list.clear(): Elimina todos los elementos de la lista.
  • list.count(x): Retorna el número de veces que x aparece en la lista.
  • list.sort(key=None,reverse=False): Ordena los elementos de la lista.
  • list.reverse(): Invierte el orden de la lista.
  • list.index(x): Devuelve el indice del primer elemento de la lista que sea x.
  • list.copy(): Devuelve una copia de la lista.

Para emular una cola con una lista se tiene el siguiente ejemplo:

>>> cola = [3,5,6]


>>> cola.append(8)

>>> cola

[3, 5, 6, 8]

>>> cola.pop()

8

>>> cola.append(8)

>>> cola.pop(0)

3

>>> cola.append(9)

>>> cola.pop(0)

5

>>> cola.pop(0)

6

>>> cola = [3,5,6]

>>> cola

[3, 5, 6]

>>> cola.append(9)

>>> cola

[3, 5, 6, 9]

>>> cola.append(19)

>>> cola

[3, 5, 6, 9, 19]

>>> cola.pop(0)

3

>>> cola.pop(0)

5

>>> cola

[6, 9, 19]

>>> cola.pop(0)

6

>>> cola

[9, 19]

>>> cola.pop(0)

9

>>> cola

[19]

>>> cola.pop(0)

19

>>> cola

[]


Se usa el método pop diciendo que tome siempre el primer elemento de la lista.

Esto no es eficiente así que la mejor opción es implementar una cola con collection.deque:

>>> from collections import deque
>>> cola = deque([1,2,3,4,5])
>>> cola
deque([1, 2, 3, 4, 5])
>>> cola.append(19) #Llega 19
>>> cola
deque([1, 2, 3, 4, 5, 19])
>>> cola.append(39) #Llega 39
>>> cola
deque([1, 2, 3, 4, 5, 19, 39])
>>> cola.popleft() #Sale 1
1
>>> cola.popleft() #Sale 2
2
>>> cola
deque([3, 4, 5, 19, 39])
>>> cola.append(51) #Llega 51
>>> cola
deque([3, 4, 5, 19, 39, 51])
>>> cola.popleft() #Sale 3
3
>>> cola
deque([4, 5, 19, 39, 51])



En este artículo se demostró como manejar colas sólo con listas o con collections.deque.

5 feb. 2017

Volviendo a lo básico, POO en Python ( diferencia entre __init__ y __new__) (parte 10)

Continuando con la serie de artículos volviendo a lo básico POO, en este artículo se toca el tema de las diferencias entre __init__ y __new__.

Este artículo se baja en un artículo en inglés entendiendo new e init.

Muchos que han programado orientado a objetos en Python nunca han usado el método __new__, el que usan como constructor es el __init__.

La realidad es que el método __init__ crea el objeto y luego lo inicializa, no es el constructor como tal, en cambio el método __new__ sólo construye el objeto.

Para este artículo se trabajará con Python 3.


Se tiene la siguiente clase:


class A(object):

    pass



Así que la Clase A hereda de object en Python 3.


Ahora se crea una clase que use __new__ e __init__:


class A(object):
    def __new__(cls):
        print "A.__new__ es llamado"
        return super(A, cls).__new__(cls)
    def __init__(self):
        print "A.__init__ es llamado"
A()

La salida es la siguiente:

A.__new__ es llamado
A.__init__ es llamado


Ahora vamos con un ejemplo donde __init__ no será llamado:



class A(object): def __new__(cls): print "A.__new__ es llamado" def __init__(self): print "A.__init__ es llamado" print ("Solo llamando el objeto A() o instanciandolo") A() a = A() print ("*"*40) print ("Ejecutando un print con el objeto A()") print (A())


Como se puede ver, en el método __init__ se está ejecutando un print y en el método __new__ también.

La salida es la siguiente:

Solo llamando el objeto A() o instanciandolo
A.__new__ es llamado
A.__new__ es llamado
****************************************
Ejecutando un print con el objeto A()
A.__new__ es llamado
None


Como se puede ver, al llamar a A() o al instanciarlo, sólo se muestra la salida del  método __new__ , y el de __init__ no, sólo cuando se ejecuta el print(A()) es que devuelve la salida del método __new__ y devuelve None el método __init__.


Ahora se mostrará la clase con sólo el método __new__ donde se ejecuta un print y retorna un número:



class A(object):
    def __new__(cls):
        print "A.__new__ es llamado"
        return 29
print ("Solo llamando al objeto")
A()
print ("*"*30)
print ("Llamando al objeto desde un print")
print A()

La salida es la siguiente:

Solo llamando al objeto
A.__new__ es llamado
******************************
Llamando al objeto desde un print
A.__new__ es llamado
29


Al sólo llamar al objeto sólo muestra el print, pero al ejecutar el print muestra el print del método y el entero que retorna.

Ahora se hará lo mismo pero con el método __init__:



class A(object):



    def __init__(self):

        print "A.__init__ es llamado"

        return 33





try:

    print ("Solo llamando al objeto")

    A()

except (TypeError):

    print ("Error de tipo")


La salida es la siguiente:

Solo llamando al objeto
A.__init__ es llamado
Error de tipo


En este caso el return devuelve error de tipo. La única forma que en el método __init__ devuelva algo es que sea del tipo None.

Ahora se muestra el uso de una clase alternativa llamada Ejemplo:

class Ejemplo(object):
    def __str__(self):
        return "Ejemplo"
class A(object):
    def __new__(cls):
        return Ejemplo()
class B(object):
    def __new__(cls):
        return super(B, cls).__new__(Ejemplo)
print ("Prueba con A")
print A()
print ("Prueba con B")
print B()


La salida devuelve lo siguiente:

Prueba con A
Ejemplo
Prueba con B
Ejemplo


Como puede verse, hay casos donde es útil utilizar __new__ y otros donde se pueden seguir usando __init__.

El método __new__ es usado en patrones de diseño con python, que será la siguiente serie de artículos que vendrán en este blog.



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.



31 oct. 2016

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

Los artículos anteriores sobre Programación Orientada a Objetos los pueden revisar en el enlace.

En este artículo se tocará el tema de la herencia, la encapsulación, el uso de getter, setter y deleter, el uso de método estático y y classmethod.

En el artículo de la parte 3 de POO se explica el uso de @property y @método.setter, falto explicar el deleter que sirve para borrar atributos del objeto.

El @staticmethod se usa cuando no se está usando el argumento self en un método.

El @classmethod define un método que se le pasa entre los argumentos a la clase (por convención se define como cls).

Se creará la clase Punto2D con los siguientes atributos y métodos:

  • Se define al punto como (x,y), la clase Punto2 tiene los atributos privados (estos atributos no se pueden acceder a ellos desde la instancia de la clase, por esa razón se tiene el getter y setter):
    • self.__x
    • self.__y
  • Método __init__ que le asigna los valores inciales a la instancia.
  • getter, setter y deleter de punto.
  • getter, setter y deleter de ValorX y ValorY.
  • Método mover: Asigna nuevo valor a punto.
  • Método reset: Asigna el valor (0,0) al punto. 
  • Método distanciaOtroPunto: Cálcula la distancia con otro punto.



El código de ej12.py se muestra a continuación:



#!/usr/bin/env python3

# -*- coding: utf-8 -*-



from math import sqrt







class Punto2D(object):

    """Representacion de un punto en 2 dimensiones"""

    cont_puntos = 1



    def __init__(self,punto=(0,0)):

        '''constructor del punto'''

        self.__x,self.__y = punto

        Punto2D.cont_puntos += 1



    @property

    def ValorX(self):

        '''Devuelve el valor de X'''

        return self.__x



    @ValorX.setter

    def ValorX(self,x):

        '''Se le asigna un valor  a x por medio de setter'''

        self.__x = x



    @property

    def ValorY(self):

        '''Devuelve el valor de y'''

        return self.__y



    @ValorY.setter

    def ValorY(self,y):

        '''Se le asigna un valor a y por medio del setter'''

        self.__y = y



    @property

    def punto(self):

        """el getter de punto, devuelve el punto"""

        return (self.__x,self.__y)



    @punto.setter

    def punto(self,punto):

        ''''el setter de punto'''

        self.__x,self.__y = punto



    @property

    def cantPuntos(self):

        '''Devuelve la cantidad de puntos creados'''

        return Empleado.cont_puntos



    @punto.deleter

    def punto(self):

        '''deleter de punto'''

        del self.__x

        del self.__y

        cont_puntos = 0



    def mover(self,x,y):

        '''mueve el punto a un nuevo punto'''

        self.__x, self.__y = x,y



    def reset(self):

        '''coloca en el origen al punto'''

        self.mover(0,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 )



    @staticmethod

    def autor(autor):

        '''Se define el autor de la clase'''

        return "El autor de esta clase es: {}".format(autor)



    @classmethod

    def cantidadPuntos(cls,cantidad):

        '''Se cambia la cantidad de puntos'''

        cls.cont_puntos = cantidad



if __name__ == "__main__":

    #Se crea cordenada de la clase Punto2D donde se le pasa (4,6)

    cordenada = Punto2D((4,6))

    #Se muestra el valor del punto.

    print(cordenada.punto)

    #Se asigna por setter un nuevo valor

    cordenada.punto = (10,15)

    #Se muestra el nuevo valor de cordenada

    print(cordenada.punto)

    #Se calcula la distancia con respecto a otro punto.

    print(cordenada.distanciaOtroPunto(Punto2D((100,100))))

    #Se fija el punto en un valor (0,0)

    cordenada.reset()

    #Se muestra el nuevo valor de la cordenada.

    print (cordenada.punto)

    #Se muestra un mensaje pasando el autor de la clase.

    print (cordenada.autor("Ernesto"))

    #Muestra la cantidad de puntos que se han creado.

    print ('Cantidad de puntos : {0}'.format(cordenada.cont_puntos ))

    #Se modifica la cantidad de puntos a 1 por medio de setter.

    Punto2D.cantidadPuntos(1)

    #Se vuelve a mostrar la cantida de puntos.

    print ('Cantidad de puntos : {0}'.format(cordenada.cont_puntos ))

    #Se borra la instancia cordenada.

    del(cordenada)

    #Se intenta mostrar el punto pero va a devolver que no se puede

    try:

        print (cordenada.punto)

    except (NameError):

        print ("No existe el objeto cordenada")



Al ejecutar ej12.py se tiene lo siguiente:

python ej12.py
(4, 6)
(10, 15)
123.794184031
(0, 0)
El autor de esta clase es: Ernesto
Cantidad de puntos : 3
Cantidad de puntos : 1
No existe el objeto cordenada



Ahora se va a crear ej12_3d.py que tendrá la clase Punto3D que hereda de Punto2D.

La clase Punto3D tiene practicamente los mismos métodos, lo único distinto es que ahora maneja un punto con 3 dimensiones, para ello hereda del punto en 2 dimensiones X y Y y maneja Z. 

Así que se sobrecarga los métodos de Punto2D en Punto3D. Para poder acceder al objeto Punto2D se usa super. 

A continuación el código de ej12_3d.py:



#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#Se importa la clase Punto2D de ej12.py
from ej12 import Punto2D
#Se importa raiz cuadrada
from math import sqrt
class Punto3D(Punto2D):
    '''Se define la clase Punto3D'''
    def __init__(self,punto3d):
        '''Método init donde se le asigna x y y a Punto2D por medio de super'''
        super(Punto3D,self).__init__(punto3d[0:-1])
        self.__z = punto3d[-1]
    @property
    def ValorZ(self):
        '''devuelve el valor de z por medio de getter'''
        return self.__z
    @ValorZ.setter
    def ValorZ(self,z):
        '''Se asigna un valor a z por medio de setter'''
        self.__z = z
    @ValorZ.deleter
    def ValorZ(self):
        '''se borra el valor de z por medio de deleter'''
        del (self.__z)
    @property
    def punto(self):
        '''el getter de punto, devuelve el punto'''
        x,y = super(Punto3D,self).punto
        return (x,y,self.ValorZ)
    @punto.setter
    def punto(self,punto3d):
        '''Se le asigna un valor al punto por medio de setter'''
        super(Punto3D,self).mover(punto3d[0],punto3d[1])
        self.__z = punto3d[-1]
    @punto.deleter
    def punto(self):
        '''Se borra el punto por medio de deleter'''
        print ("Punto borrado")
        del Punto2D
        del self.__z
    def mover(self,x,y,z):
        '''Se asigna un nuevo valor al punto''''
        super(Punto3D,self).mover(x,y)
        self.__z = z
    def reset(self):
        '''Se fija el punto como (0,0,0)'''
        super(Punto3D,self).mover(0,0)
        self.__z = 0
    def distanciaOtroPunto(self,oPunto):
        '''devuelve la distancia entre el punto original y un punto dado'''
        x,y = super(Punto3D,self).punto
        z = self.__z
        return sqrt((x - oPunto.punto[0])**2 + (y - oPunto.punto[1])**2 + (z - oPunto.ValorZ) ** 2 )
if __name__ == '__main__':
    #Se crea la instancia Punto3D con el punto.
    punto3d = Punto3D((4,6,4))
    #Se muestra el valor del punto
    print(punto3d.punto)
    #Se asigna un nuevo valor a punto.
    punto3d.punto = (3,3,3)
    #Se muestra dicho valor
    print(punto3d.punto)
    #Se muestra los valores de x,y y z.
    print(punto3d.ValorX)
    print(punto3d.ValorY)
    print(punto3d.ValorZ)
    #Se asigna el valor (0,0,0)
    punto3d.reset()
    #Se muestra su valor.
    print(punto3d.punto)
    #Se calcula la distancia con otro punto.
    print(punto3d.distanciaOtroPunto(Punto3D((15,45,55))))
    #Se borra el punto3d.
    del(punto3d)
    #Se intenta mostrar el valor o devuelve que el objeto no existe.
    try:
        print (punto3d.punto)
    except (NameError):
        print ("No existe el objeto punto3d")



Al ejecutar ej12_3d.py se obtiene lo siguiente:

python ej12_3d.py
(4, 6, 4)
(3, 3, 3)
3
3
3
(0, 0, 0)
72.6291952317
No existe el objeto punto3d



En este artículo se muestra la encapsulación, la herencia y varios métodos.

Los códigos lo pueden encontrar en gitlab tanto para ej12.py como para ej12_3d.py.


12 oct. 2016

API Rest Ful con Flask y MongoDB (Flask-MongoAlchemy y Flask-restful)

 API Rest Ful con Flask y MongoDB (Flask-MongoAlchemy y Flask-restful)


En el artículo anterior se explicó como hacer el API Rest Ful usando HTTP con los métodos GET, POST, PUT y DELETE, sin usar una librería para el Rest Ful. 

En este artículo se usará la librería Flask-RestFul, su documentación la pueden revisar acá

Estructura de archivos y directorios del proyecto


La estructura de archivos y directorios sigue siendo la misma que en el artículo anterior:

tutorial-flask/
├── app
│   └── run.py
├── docker-compose.yml
├── Dockerfile
└── README.md



Archivo Dockerfile y docker-compose.yml


Al archivo Dockerfile se le agrega que se instale flask-restful, a continuación el archivo:



FROM python
WORKDIR /code/
RUN pip3 install --upgrade pip
RUN pip3 install  pymongo
RUN pip3 install Flask
RUN pip3 install Flask-PyMongo
RUN pip3 install Flask-MongoAlchemy
RUN pip3 install Flask-restful
EXPOSE 5000
ADD ./app/* /code/
COPY ./app/* /code/
CMD python run.py
El archivo docker-compose.yml si es el mismo del artículo anterior:

flask-rest1:
  build: .
  ports:
    - "5000:5000"
  volumes:
    - "./app/:/code"
  links:
    - mongo
mongo:
  image: mongo
  ports:
    - "27017:27017"
  volumes:
    - "/srv/data/db:/data/db:rw"



Código de la aplicación


Se usarán los mismos métodos HTTP del artículo anterior (GET,POST,PUT y DELETE), el url para las consultas será /empleados y la forma de pasar argumentos por el url es pasando el nombre del empleado /empleados/<string:nombre>  . 

Se crean dos clases:
  • EmpleadoList: Esta clase no se le pasa argumentos por URL, tiene dos métdos GET y POST:
    • GET: Permite listar los empleados que existen en la base de datos.
    • POST: Permite insertar un empleado a la base de datos pasando un json con los datos.
  • Empleado: que a los métodos se le pasa el nombre de un empleado. Se define los métodos GET, PUT y DELETE:
    • GET: Permite buscar un empleado pasando su nombre, se devuelve un json con sus datos.
    • PUT: Permite actualizar la información de un empleado, pasando su nombre y el json con los datos a modificar, devuelve un jso con todos los empleados.
    • DELETE: Permite borrar un empleado de la base de datos, se le pasa el nombre del empleado, devuelve un json con todos los empleados.


El código de app/run.py se muestra a continunación:



#!/usr/bin/env python
#Se importa Flask, reqest y jsonify
from flask import Flask, request,jsonify, Response
#Se importa MongoAlchemy
from flask_mongoalchemy import MongoAlchemy
#Se importa dumps
from bson.json_util import dumps
#rom flask_restful import Resource, Api
from flask_restful import reqparse, abort, Api, Resource
#Se instancia la clase de Flask, se configura el acceso
#a la base de datos mongodb a empleados
app = Flask(__name__)
app.config['MONGOALCHEMY_DATABASE'] = 'empleados'
app.config['MONGOALCHEMY_CONNECTION_STRING'] = 'mongodb://mongo:27017/empleados'
#Se instancia mongoalchemy pasando la app.
db = MongoAlchemy(app)
#Se asocia el API a la aplicacion
api = Api(app)
#Se crea la clase empleados la cual manejara los documentos.
class empleados(db.Document):
    nombre = db.StringField()
    sexo = db.StringField()
    edad = db.IntField()
    dni = db.IntField()
#Se define la funcion de pagina no encontrada.
@app.errorhandler(404)
def not_found(error=None):
    mensaje = {
            'status': 404,
            'message': 'Not Found: ' + request.url,
    }
    resp = Response(jsonify(mensaje),status=404,mimetype='application/json')
    return resp
#Clase EmpleadosList que permite listar los empleados o insertar un empledo.
#Se definen los metdos get y post
class EmpleadosList(Resource):
    #Se define el metodo get el cual devuelve un json con todos los empleados.
    def get(self):
        #Se realiza la busqueda y se devuelve el resultado, si existe un error de atributo (que el empleado no existe)
        #Se devuelve empleado no encontrado.
        try:
            consulta = empleados.query.all()
            resultado = []
            for i in consulta:
                resultado.append(i.wrap())
            resp =  Response(dumps(resultado),status=200,mimetype='application/json')
            resp.headers['Link'] = 'http://blog.crespo.org.ve'
            return resp
        except (AttributeError):
            return not_found
    #Se define el metodo post para agregar un empleado por medio de un json a la
    #base de datos mongoDB.
    def post(self):
        #args = parser.parse_args()
        #Se crea la instancia empleado de la clase empleados donde se
        #logra hacer la inserción de un empleado con el metodo save.
        nombre = str(request.json['nombre'])
        sexo = str(request.json['sexo'])
        edad = int(request.json['edad'])
        dni = int(request.json['dni'])
        empleado = empleados(nombre=nombre,sexo=sexo,edad=edad,dni=dni)
        empleado.save()
        #Se retorna que el usuario fue agregado.
        ###
        consulta = empleados.query.all()
        listado = []
        for i in consulta:
            listado.append(i.wrap())
        resp =  Response(dumps(listado),status=201,mimetype='application/json')
        resp.headers['Link'] = 'http://blog.crespo.org.ve'
        return resp
#Se crea la Clase Empleado que hereda de Resource
#Tiene los metodos get, put y delete
class Empleado(Resource):
    #Se define el metodo get, permite buscar un empleado por su nombre
    def get(self,nombre):
        #Se realiza la busqueda y se devuelve el resultado, si existe un error de atributo (que el empleado no existe)
        #Se devuelve empleado no encontrado.
        try:
            resultado = empleados.query.filter(empleados.nombre == nombre).first()
            return dumps({'nombre':resultado.nombre,'sexo':resultado.sexo,'edad':resultado.edad,'dni':resultado.dni}),200,{'Content-Type':'application/json'}
        except (AttributeError):
            return not_found
    #Se define el metodo put que permite actualizar la informacion de un empleado
    #pasando su nombre, los datos a modificar se pasan en un json.
    def put(self,nombre):
        #Se intenta buscar al empleado en la base de datos, si no esta devuelve error
        try:
            #Se consulta en la base de datos, donde devuelve el primer elemento encontrado
            resultado = empleados.query.filter(empleados.nombre == nombre).first()
            #Se toma los datos de un json y se guardan en sus variables, salvando luego
            #en la base de datos.
            resultado.sexo = str(request.json['sexo'])
            resultado.edad = int(request.json['edad'])
            resultado.dni = int(request.json['dni'])
            resultado.save()
            #Se realiza la consulta desplegando los empleados
            consulta = empleados.query.all()
            listado = []
            for i in consulta:
                listado.append(i.wrap())
            #Se devuelve la nueva lista de empleados en un json.
            resp =  Response(dumps(listado),status=201,mimetype='application/json')
            resp.headers['Link'] = 'http://blog.crespo.org.ve'
            return resp
        except (AttributeError):
            return not_found
    #Se define el metodo delete que permite borrar un empleado de la base de datos
    #pasando el nombre del empleado.
    def delete(self,nombre):
        #Se busca el empleado, si existe se borra de la base de datos y se devuelve
        #mensaje de empleado borrado, si no, se devuelve el mensaje de empleado no
        #encontrado.
        try:
            resultado = empleados.query.filter(empleados.nombre == nombre).first()
            resultado.remove()
            ###
            consulta = empleados.query.all()
            listado = []
            for i in consulta:
                listado.append(i.wrap())
            resp =  Response(dumps(listado),status=200,mimetype='application/json')
            resp.headers['Link'] = 'http://blog.crespo.org.ve'
            return resp
        except (AttributeError):
            return not_found
#Se define las rutas para los recursos con las clases asociadas:
#/empleado
#/empleado/<string:nombre>
api.add_resource(EmpleadosList,'/empleado')
api.add_resource(Empleado,'/empleado/<string:nombre>')
if __name__ == "__main__":
    #Se corre la aplicacion en modo debug
    app.run(host="0.0.0.0",debug=True)


Construcción de la imagen y ejecución del contenedor


Construcción de la imagen Docker:

docker-compose build


Ejecución del contenedor Docker:

docker-compose up


Prueba del API Rest Ful

Se usará postman para consultar al API.

Listar todos los empleados

Se abre postman en el url http://localhost:5000/empleado con método GET, a continuación se muestra una captura de pantalla:


El JSON que se devuelve es el siguiente:

[{"edad": 29, "sexo": "Femenino", "nombre": "Jane Doe", "dni": 8, "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "sexo": "Masculino", "nombre": "John Doe", "dni": 7, "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "sexo": "Masculino", "nombre": "Pedro Perez", "dni": 6, "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "sexo": "Femenino", "nombre": "Petra", "dni": 5, "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "sexo": "Masculino", "nombre": "Luis Gonzalez", "dni": 4, "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "sexo": "Femenino", "nombre": "Luissana", "dni": 2, "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 42, "sexo": "Masculino", "nombre": "Neg", "dni": 1, "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "sexo": "Femenino", "nombre": "Dayana", "dni": 1050, "_id": {"$oid": "57f64d7d557e3f00086651e8"}}]

Agregar un empleado


Se abre postman en el URL http://localhost:5000/empleado con método POST y se pasa el siguiente JSON:

{
"nombre": "Nadir",
"sexo": "Masculino",
"edad": 45,
"dni": 11059
}



A continuación se muestra la captura de pantalla de la colocación de los datos:


Al ejecutar la acción se tiene el siguiente resueltado (como lo muestra la siguiente captura de pantalla):



El JSON que se devuelve es el siguiente:

[{"edad": 29, "sexo": "Femenino", "nombre": "Jane Doe", "dni": 8, "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "sexo": "Masculino", "nombre": "John Doe", "dni": 7, "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "sexo": "Masculino", "nombre": "Pedro Perez", "dni": 6, "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "sexo": "Femenino", "nombre": "Petra", "dni": 5, "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "sexo": "Masculino", "nombre": "Luis Gonzalez", "dni": 4, "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "sexo": "Femenino", "nombre": "Luissana", "dni": 2, "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 42, "sexo": "Masculino", "nombre": "Neg", "dni": 1, "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "sexo": "Femenino", "nombre": "Dayana", "dni": 1050, "_id": {"$oid": "57f64d7d557e3f00086651e8"}}, {"edad": 45, "sexo": "Masculino", "nombre": "Nadir", "dni": 11059, "_id": {"$oid": "57fe2800d76747000b2ef40f"}}]


Buscar un empleado

Para buscar un empleado se pasa el siguiente URL http://localhost:5000/empleado/Nadir con método GET al postman, a continuación se muestra una captura de pantalla del resultado:


El JSON que devuelve es el siguiente:

"{\"edad\": 45, \"sexo\": \"Masculino\", \"nombre\": \"Nadir\", \"dni\": 11059}"


Actualizar empleado


Para actualizar al empleado Nadir se pasará el siguiente url a postman http://localhost:5000/empleado/Nadir con método PUT, y se pasará el siguiente json:

{
"sexo": "Masculino",
"edad": 35,
"dni": 11059
}

La captura de pantalla muestra los datos que se cargaron a postman:







Al ejecutar la acción se tiene la siguiente captura de pantalla:



El JSON que devuelve es el siguiente:

[{"edad": 29, "sexo": "Femenino", "nombre": "Jane Doe", "dni": 8, "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "sexo": "Masculino", "nombre": "John Doe", "dni": 7, "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "sexo": "Masculino", "nombre": "Pedro Perez", "dni": 6, "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "sexo": "Femenino", "nombre": "Petra", "dni": 5, "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "sexo": "Masculino", "nombre": "Luis Gonzalez", "dni": 4, "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "sexo": "Femenino", "nombre": "Luissana", "dni": 2, "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 42, "sexo": "Masculino", "nombre": "Neg", "dni": 1, "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "sexo": "Femenino", "nombre": "Dayana", "dni": 1050, "_id": {"$oid": "57f64d7d557e3f00086651e8"}}, {"edad": 35, "sexo": "Masculino", "nombre": "Nadir", "dni": 11059, "_id": {"$oid": "57fe2800d76747000b2ef40f"}}]

Como se puede notar los datos del empleado Nadir en lo que respecta a su edad ha cambiado.

Borrar empleado

Para terminar se borrará el empleado Nadir de la base de datos, se pasa el url http://localhost/empleado/Nadir con método DELETE, a continuación se muestra el resultado en el postman:


El JSON que devuelve es el siguiente:

[{"edad": 29, "sexo": "Femenino", "nombre": "Jane Doe", "dni": 8, "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "sexo": "Masculino", "nombre": "John Doe", "dni": 7, "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "sexo": "Masculino", "nombre": "Pedro Perez", "dni": 6, "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "sexo": "Femenino", "nombre": "Petra", "dni": 5, "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "sexo": "Masculino", "nombre": "Luis Gonzalez", "dni": 4, "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "sexo": "Femenino", "nombre": "Luissana", "dni": 2, "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 42, "sexo": "Masculino", "nombre": "Neg", "dni": 1, "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "sexo": "Femenino", "nombre": "Dayana", "dni": 1050, "_id": {"$oid": "57f64d7d557e3f00086651e8"}}]

Como se puede ver el empleado Nadir ya no existe en la base de datos.


Para terminar se muestra la captura de pantalla del contenedor ejecutandose:



Se mantiene resaltado la salida de la ejecución de Flask ( a continuación se muestra el texto):

flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:02:56] "GET /empleado HTTP/1.1" 200 -
mongo_1       | 2016-10-12T12:09:37.175+0000 I COMMAND  [conn2] update empleados.empleados query: { _id: ObjectId('57fe2800d76747000b2ef40f') } update: { _id: ObjectId('57fe2800d76747000b2ef40f'), edad: 45, sexo: "Masculino", nombre: "Nadir", dni: 11059 } keysExamined:0 docsExamined:0 nMatched:1 nModified:1 upsert:1 keyUpdates:0 writeConflicts:0 numYields:1 locks:{ Global: { acquireCount: { r: 2, w: 2 } }, Database: { acquireCount: { w: 2 } }, Collection: { acquireCount: { w: 2 } } } 326ms
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:09:37] "POST /empleado HTTP/1.1" 201 -
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:12:40] "GET /empleado/Nadir HTTP/1.1" 200 -
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:18:23] "POST /empleado/Nadir HTTP/1.1" 405 -
mongo_1       | 2016-10-12T12:20:00.777+0000 I COMMAND  [conn2] update empleados.empleados query: { _id: ObjectId('57fe2800d76747000b2ef40f') } update: { _id: ObjectId('57fe2800d76747000b2ef40f'), edad: 35, sexo: "Masculino", nombre: "Nadir", dni: 11059 } keysExamined:1 docsExamined:1 nMatched:1 nModified:1 keyUpdates:0 writeConflicts:0 numYields:2 locks:{ Global: { acquireCount: { r: 3, w: 3 } }, Database: { acquireCount: { w: 3 } }, Collection: { acquireCount: { w: 3 } } } 124ms
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:20:00] "PUT /empleado/Nadir HTTP/1.1" 201 -
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:23:19] "DELETE /empleado/Nadir HTTP/1.1" 200 -



Con esto se ha mejorado la forma de como crear un API Rest, en este caso usando flask-restful. 

El repositorio del proyecto se encuentra en el repositorio gitlab tutorial-flask en la rama mongo-flask-restful.


AddThis