31 dic. 2015

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

Continuando con algunos conceptos de POO en Python, se tienen los artículos anteriores:

  1. Volviendo a lo básico, POO en Python (parte 1)
  2. Volviendo a lo básico, POO en Python (parte 2)



En esté artículo se muestra el uso de __getattr__ (se ejecuta al acceder a un atributo que no existe), como obtener valores y modificar valores, y como se simplifica con el uso del decorador @property.


Se creará la clase Punto donde se mostrará el uso de __getattr__:

class Punto(object):

 

 def __init__(self,x=3,y=5):

  self._x = x

  self._y = y

 

 def __getattr__(self,attr):

  print "No se puede acceder a un atributo invalido"



 



if __name__ == "__main__":

 punto = Punto(5,5)

 punto.var

 

Al ejecutar el programa se tiene:

$ python ej6.py 
No se puede acceder a un atributo invalido


Al intentar acceder a un atributo no existente se ejecuta el método __getattr__ donde se muestra en pantalla el mensaje no se puede acceder a un atributo invalido.



El siguiente código se vuelve a mostrar la clase punto pero está vez se tiene dos métodos, uno que muestra el valor del punto y otro que modifica el valor del punto y se usará la función interna property.

La sintaxis de la función property es:
property(getter,setter,delete,doc).

El código a continuación:


class Punto(object):

 def __init__(self,x,y):

  self._x = x

  self._y = y



 def get_punto(self):

  return (self._x, self._y)



 def set_punto(self,punto):



  self._x, self._y = punto



 punto = property(get_punto,set_punto)





if __name__ == "__main__":

 punto = Punto(4,6)

 print(punto.punto)

 punto.punto = (10,15)

 print(punto.punto)

Al ejecutar el programa se tiene:

$ python ej7.py 
(4, 6)
(10, 15)


Como último ejemplo de uso de getter y setter se muestra el uso del decorador @property, y como simplifica el uso de property con respecto al código anterior:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class Punto(object):
def __init__(self,punto):
self._x,self._y = punto
@property
def punto(self):
return (self._x,self._y)
@punto.setter
def punto(self,punto):
self._x,self._y = punto
if __name__ == "__main__":
cordenada = Punto((4,6))
print(cordenada.punto)
cordenada.punto = (10,15)
print(cordenada.punto)
Al ejecutar el programa se tiene: 

$ python ej8.py 
(4, 6)
(10, 15)

Lo primero que se hace es definir un método punto donde se retorna una tupla con el valor de x y de y, a este método se le coloca el decorador property. Luego se crea otro método llamado punto donde se toma un punto y se le asigna a x y a y pero definiendo un decorador @punto.setter el primer método hace el trabajo del getter y el segundo del setter. 



Referencia:

  1. Python Avanzado
  2. Ejemplos de referencia (módulos y tareas)





28 dic. 2015

Uso de docker en Debian Jessie (parte 2)

Los artículos anteriores sobre docker son:

En este artículo se explicará como mantener ejecutando una instancia, como iniciar las instancias automáticamente, acceder a los servicios que se ejecutan en el contenedor y ejecutar la imagen desde diferentes máquinas al registrar el contenedor.

Del artículo anterior se tiene 2 imagenes en el equipo, una de Debian y la otra creada por mi persona que viene del repositorio de docker hub: 

$ docker images 
REPOSITORY                  TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ecrespo/empaquetadodebian   latest              ee5883957d64        18 hours ago        656.9 MB
debian                      latest              8b9a99209d5c        3 weeks ago         125.1 MB

Para ejecutar apache y que quede funcionando en primer plano de la imagen que se construyo, se usa la opción -D y la -d para que se ejecute sin adjuntar.

$docker run -d   ecrespo/empaquetadodebian /usr/sbin/apache2ctl -D FOREGROUND
eeb8041b4331e572b3e452fc00da5d364115ba8a08ad015aa05eb1d243a1bf4b

Como se nota devuelve un hash. Ahora se revisa los procesos del docker y se tiene el contenedor con apachectl ejecutandose:

$ docker ps 
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS               NAMES
eeb8041b4331        ecrespo/empaquetadodebian   "/usr/sbin/apache2ctl"   4 minutes ago       Up 4 minutes                            tender_goodall


Para detener un proceso se toma el id del contenedor ejecutando docker stop:

$ docker stop eeb8041b4331
eeb8041b4331

Al volver a listar los procesos se tiene que ya no hay contenedores corriendo:
$ docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES


$ docker run -d -p 80:80 ecrespo/empaquetadodebian /usr/sbin/apache2ctl -D FOREGROUND
8c7d49ac2e671e0f7dd1bbfb038f5a23ff06e3120d07d059c0cde99dc76f1a79

Al volver a listar los procesos  se tiene el contenedor corriendo apache2ctl en el puerto 80:

$ docker ps 
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                NAMES
8c7d49ac2e67        ecrespo/empaquetadodebian   "/usr/sbin/apache2ctl"   51 seconds ago      Up 45 seconds       0.0.0.0:80->80/tcp   stupefied_darwin


A continuación se abre el navegador a localhost (ojo el servicio apache debe estar apagado en el equipo) y se tiene apache corriendo como se muestra a continuación:



Se sube la imagen a los repositorios con el comando docker push:

$ docker push ecrespo/empaquetadodebian
The push refers to a repository [docker.io/ecrespo/empaquetadodebian] (len: 1)
ee5883957d64: Pushed 
8b9a99209d5c: Pushed 
6d1ae97ee388: Pushed 
latest: digest: sha256:893cb08536430d5555434bb4b120fb54e9f21da7cd87325120c1698aa57e2882 size: 4009

También se puede probar una aplicación como por ejemplo sonarqube el cual es una aplicación web hace métricas de calidad de software. La imagen la encuentran en el docker hub de sonarqube.

Se puede ejecutar sonarqube con los puertos 9000 y 9092 de la siguiente forma:

$ docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 sonarqube


Al terminar de descargar se ejecuta docker images y se nota que se tiene la imagen de sonarqube:

$ docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ecrespo/empaquetadodebian   latest              ee5883957d64        23 hours ago        656.9 MB
debian                      latest              8b9a99209d5c        3 weeks ago         125.1 MB
sonarqube                   latest              dd47274097f7        7 weeks ago         942.5 MB

Se revisa los contenedores:
$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                                            NAMES
f44317723253        sonarqube                   "./bin/run.sh"           8 minutes ago       Up 7 minutes        0.0.0.0:9000->9000/tcp, 0.0.0.0:9092->9092/tcp   sonarqube
8c7d49ac2e67        ecrespo/empaquetadodebian   "/usr/sbin/apache2ctl"   3 hours ago         Up 3 hours          0.0.0.0:80->80/tcp                               stupefied_darwin

Se tiene sonarqube corriendo en el puerto 9000, al abrir el navegador en localhost:9000 se tiene lo siguiente:



Se detiene el contenedor de sonarqube:
$ docker stop f44317723253
f44317723253

Se revisa los procesos y ya no aparece sonarqube:
$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                NAMES
8c7d49ac2e67        ecrespo/empaquetadodebian   "/usr/sbin/apache2ctl"   3 hours ago         Up 3 hours          0.0.0.0:80->80/tcp   stupefied_darwin

Si se quiere iniciar sonarqube se ejecuta docker start con el id del contenedor:
$ docker start f44317723253
f44317723253

Se lista de nuevo:
$ docker ps 
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                                            NAMES
f44317723253        sonarqube                   "./bin/run.sh"           22 minutes ago      Up About a minute   0.0.0.0:9000->9000/tcp, 0.0.0.0:9092->9092/tcp   sonarqube
8c7d49ac2e67        ecrespo/empaquetadodebian   "/usr/sbin/apache2ctl"   3 hours ago         Up 3 hours          0.0.0.0:80->80/tcp                               stupefied_darwin

Se nota que sonarqube vuelve a levantar ya con los puertos definidos.

En el próximo artículo se explicará como construir una imagen por medio del archivo dockerfile.

Referencias:

27 dic. 2015

Uso de docker en Debian Jessie (parte 1)

En el artículo anterior se explico la instalación de docker, al final del mismo se realizó una prueba de funcionamiento ejecutando una imagen llamada hello-word.

Al ejecutar el comando:

# docker run hello-world
El comando retorna:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b901d36b6f2f: Pull complete 
0a6ba66e537a: Pull complete 
Digest: sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
Status: Downloaded newer image for hello-world:latest

Hello from Docker.
This message shows that your installation appears to be working correctly.

La parte subrayada indica que la imagen no está descargada localmente y procede a descargarla.

Si se vuelve a ejecutar el comando ya el mensaje no aparece ya que la imagen hello-work ya se encuentra descargada.

$docker run hello-world

Hello from Docker.

Podemos listar las imagenes existentes con el siguiente comando: 

$ docker images 
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
hello-world                 latest              0a6ba66e537a        10 weeks ago        960 B

Se muestra el repositorio, la etiqueta que maneja la imagen, el identificador de la imagen, la fecha de creado y el tamaño de la imagen. 


Ahora probemos con una imagen que nos muestra un mensaje de la vaca que dice boo (pueden ver la info de la imagen whalesay desde acá):

$ docker run docker/whalesay cowsay boo

A continuación se muestra una imagen del resultado de la ejecución del comando anterior:


Como ven la ejecución de la imagen devuelve un resultado.

Al volver a listar las imagenes se tiene:
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
hello-world                 latest              0a6ba66e537a        10 weeks ago        960 B
docker/whalesay         latest              ded5e192a685        7 months ago        247 MB

Para eliminar una imagen se ejecuta docker rm idimagen:

$docker rmi ded5e192a685
Untagged: docker/whalesay:latest
Deleted: ded5e192a685b2c5f048ea98ca24f4c8c4dccc012185448a0247b49b24645007
Deleted: 8f17e9411cf6e7467e630b369a0216b582a8a811901befc800de41537bde1f04
Deleted: 517de05c9075311e28a12b52c38fbc4d46c47750efc71a6d88ba40a7547fd89d
Deleted: 3a2e7fe79da7865d735739288585f82c694088fc1163b415de7f5f5db12671c7
Deleted: 326bddfde6c0dd63908afd6b62b3db9cbc3d33ab23497493691e6230e051dbae
Deleted: 35217eff2e300e3b9a5402920a71ede310fe5e47d0c88d56e1d7baaa4d0aab04
Deleted: 2ce633e3e9c9bd9e8fe7ade5984d7656ec3fc3994f05a97d5490190ef95bce8d
Deleted: 98b15185dba7f85308eb0e21196956bba653cf142b36dc08059b3468a01bf35d
Deleted: 515565c29c940355ec886c992231c6019a6cffa17ff1d2abdfc844867c9080c5
Deleted: 2880a3395eded9b748c94d27767e1e202f8d7cb06f1e40e18d1b1c77687aef77

Al ejecutar docker images se tiene sólo el de hello-world:
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE


hello-world                 latest              0a6ba66e537a        10 weeks ago        960 B


Ahora ejecutaremos una imagen de Debian jessie que devuelve el mensaje de hello world:

$ docker run debian /bin/echo hello world

Unable to find image 'debian:latest' locally
Pulling repository docker.io/library/debian
8b9a99209d5c: Pulling dependent layers 
8b9a99209d5c: Download complete 
6d1ae97ee388: Download complete 
Status: Downloaded newer image for debian:latest
docker.io/library/debian: this image was pulled from a legacy registry.  Important: This registry version will not be supported in future versions of docker.
hello world

Ahora se ejecuta la misma imagen con las opciones -t y -i las cuales abren un seudo tty y lo mantienen abierto, ya  de esa forma se puede hacer ejecución de comandos dentro del contenedor:

$ docker run -t -i debian /bin/bash
root@10668b1e1336:/#

Al ejecutar en otra consola docker ps nos muestra la ejecución del contenedor de debian:

$ docker ps 



$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
debian              latest              8b9a99209d5c        3 weeks ago         125.1 MB

Como se muestra, la imagen de Debian tiene un contenedor. 

En este contenedor se instalará algunos paquetes de Debian que permiten crear paquetes de Debian, subiré la imagen a mi cuenta en los repositorios de docker. 
 
root@10668b1e1336:/etc/apt# apt install quilt devscripts pbuilder git-buildpackage apache2 lintian cdbs autotools-dev config-package-dev reprepro equivs config-package-dev debaux dput elida dh-make piuparts dpatch patch patchutils  dpkg-dev build-essential debhelper openssh-server vim mc

He creado en Docker hub un repositorio con mi cuenta que manejará la imagen que modifique, el enlace es el siguiente. Ahí se muestra el usuario/nombre del repositorio, en este caso ecrespo/empaquetadodebian.

Se muestra la imagen del sitio del repositorio:


Para subir los cambios se tiene el id del contenedor que se obtiene de ejecutar docker ps:

$ docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
10668b1e1336        debian              "/bin/bash"         2 hours ago         Up 2 hours                              cranky_wright

El comando para hacer commit al repositorio de docker es:

$ docker commit 10668b1e1336 ecrespo/empaquetadodebian
ee5883957d643419258c19c10b38d6c5e510bb2b369949be7152571083b4b421

Al salirse del contenedor ya no se tiene un contenedor activo:

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Y al listar las imagenes se tiene la imagen creada:

$ docker images 
REPOSITORY                  TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ecrespo/empaquetadodebian   latest              ee5883957d64        4 minutes ago       656.9 MB
debian                      latest              8b9a99209d5c        3 weeks ago         125.1 MB


En el siguiente artículo se explicará como usar el archivo dockerfile para facilitar la construcción. 


25 dic. 2015

Instalar Docker en Debian Jessie


Hace un tiempo me toco dictar un curso de empaquetado para Debian y un compañero preparo una máquina virtual de Virtualbox con vagrant (un tutorial lo pueden revisar en el siguiente enlace).

Hubo un momento que dañamos algunos paquetes y recuperamos la imagen de forma rápida para continuar con el curso.

En mi caso como mi máquina personal no tengo como manejar varias máquinas virtuales con virtualbox preferí irme hacía la tecnología de contenedores con Docker. Pueden revisar wikipedia para saber más de docker.

La guía de instalación para Debian Jessie en inglés lo pueden encontrar en el siguiente enlace; y la guía de usuario en el siguiente enlace. El repositorio donde se alojan imágenes lo pueden revisar en este enlace.

0. Instalar soporte de https para los repositorios:
#apt-get install apt-transport-https

1. Borrar paquetes viejos o de lxc:
#apt-get purge lxc-docker* 
#apt-get purge docker.io*

2. Agregar la llave gpg de docker.io:
#apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys  58118E89F3A912897C070ADBF76221572C52609D 

3. Agregar el repositorio al sources.list.d:
vim /etc/apt/sources.list.d/docker.list

deb https://apt.dockerproject.org/repo debian-jessie main


4. Actualizar la lista de repositorios: 
#apt-get update

5. Verificar que se baja el paquete del repositorio correcto: 
#apt-cache policy docker-engine

6. Instalar docker:
#apt-get install docker-engine

7. Iniciar el demonio docker:
#service docker start

8. Verificar que se instaló correctamente:
# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b901d36b6f2f: Pull complete 
0a6ba66e537a: Pull complete 
Digest: sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
Status: Downloaded newer image for hello-world:latest

Hello from Docker.
This message shows that your installation appears to be working correctly.

9. Dar acceso de docker a usuarios no root:

9.1 Agregar el grupo docker si no existe:
$sudo groupadd docker

9.2 Agregar al usuario al grupo docker:
$ sudo gpasswd -a ${USER} docker

9.3 Reiniciar el demonio docker:
$sudo service docker restart

En siguientes artículos se mostrará el uso de docker, como crear imágenes y por último se creará un entorno para empaquetar para Debian.
Dejaré por acá las referencias para usarlas en un futuro:
Referencias:
10. Algunos artículos del blog de Javier Garzas: 


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

Continuando con la serie de artículos sobre programación orientada objetos con python, les dejo el enlace del primer artículo.

El siguiente artículo se basa en un artículo de un blog publicado en www.toptal.com.

En el artículo anterior se muestra las variables id_ciudad y cont_ciudad como atributos de la clase. Se mostrará con el siguiente ejemplo que no siempre es bueno usar atributos de una clase, que se rompe la norma de encapsulación de la POO. 

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class Servicio(object):
dato = []
def __init__(self,otro_dato):
self.otro_dato = otro_dato
if __name__ == "__main__":
s1 = Servicio(["a","b"])
s2 = Servicio(["c","d"])
s1.dato.append(1)
print "dato de S1: ",s1.dato
print "dato de S2: ",s2.dato
s2.dato.append(2)
print "dato de s1: ",s1.dato
print "dato de s2: ",s2.dato
print "otro dato de s1: ", s1.otro_dato
print "otro dato de s2: ", s2.otro_dato


Al ejecutar el código se tiene:
dato de S1:  [1]
dato de S2:  [1]
dato de s1:  [1, 2]
dato de s2:  [1, 2]
otro dato de s1:  ['a', 'b']
otro dato de s2:  ['c', 'd']

El las 4 primeras líneas de la ejecución muestrán que la instancia s1 y la instancia s2 practicamente tienen el mismo valor a pesar de que se agrega el dato en s1 y se refleja en s2, luego se agrega el dato en s2 y se refleja en s1.

En el artículo en inglés mencionan que la solución sería no tener una lista vacía como instancia de la clase si no colocar None. 

A continuación el código:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class Servicio(object):
dato = None
def __init__(self,otro_dato):
self.otro_dato = otro_dato
if __name__ == "__main__":
s1 = Servicio(["a","b"])
s2 = Servicio(["c","d"])
s1.dato = 1
print "dato de S1: ",s1.dato
print "dato de S2: ",s2.dato
s2.dato = 2
print "dato de s1: ",s1.dato
print "dato de s2: ",s2.dato


El resultado de la ejecución es el siguiente: 
dato de S1:  1
dato de S2:  None
dato de s1:  1
dato de s2:  2

Como se ve ya no muestra valores iguales entre las dos instancias de la clase.

Ahora muestro el código sin la instancia de la clase, creando dato como una variable privada y unos métodos que acceden a dato:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class Servicio(object):
def __init__(self,otro_dato):
self.__dato = []
self.otro_dato = otro_dato
def mostrar_dato(self):
return self.__dato
def agregar_dato(self,dato):
self.__dato.append(dato)
def inicializar_dato(self,):
self.__dato = []
if __name__ == "__main__":
s1 = Servicio(["a","b"])
s2 = Servicio(["c","d"])
try:
s1.dato.append(1)
except AttributeError:
print "No se pudo agregar a dato de s1"
finally:
print s1.mostrar_dato()
print s2.mostrar_dato()
s1.agregar_dato(1)
s1.agregar_dato(3)
s2.agregar_dato(2)
s2.agregar_dato(4)
print s1.mostrar_dato()
print s2.mostrar_dato()


Al ejecutar el script se tiene lo siguiente:
No se pudo agregar a dato de s1
[]
[]
[1, 3]
[2, 4]
No es un error crear atributos de una instancia lo que se tiene que saber es exactamente que lo que se necesita en la clase para evitar estos errores, y lo mejor es seguir el paradigma de programación orientada a objetos.

18 dic. 2015

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

Luego de un tiempo escribiendo artículos de temas muy diversos sobre python, ahora iniciaré una serie sobre programación orientada a objetos desde lo básico hasta las nuevas características incorporadas en el lenguaje.

Esto me ayudará también a mí a corregir unas práctias en cuanto a la programación orientada a objetos y aprender nuevas prácticas.

Actualmente estoy usando una herramienta de una página web que permite evaluar la calidad de un código, este sitio se llama www.quantifiedcode.com.

Me he encontrado con errores en un desarrollo que por eso inicio esta serie de artículos.

En la siguiente figura verán como muestra los errores la herramienta:

Lo bueno es que al hacer un commit a github automáticamente quantifiedcode envía un correo con las incidencias.


Bueno, ahora toca ir al artículo en sí.

Se tiene una clase ciudad con varios métodos.

A continuación se muestra el código:

#!/usr/bin/env python3



from math import *



#Se crea la clase ciudad que hereda de object.

#Esto se declara asi actualmente por convencion.

class Ciudad(object):

 #Datos

 cont_ciudad = 0 

 id_ciudad = 0





 def __init__(self,nombre='',x=0,y=0):

  """Constructor que recibe el nombre de la ciudad,x y y"""

  self.nombre = nombre 

  self.x = x 

  self.y = y

  Ciudad.cont_ciudad += 1

  self.id_ciudad = Ciudad.cont_ciudad



 def __str__(self):

  """Metodo que retorna un string con la info de la ciudad"""

  return 'Ciudad: ' + self.nombre + ',id= ' + str(self.id_ciudad) + ',x= %s,y=%s' %(self.x,self.y)

    

 def __set__( self, nombre):

  """Metodo que asigna un valor"""

  self.nombre= nombre

 

 def __get__( self):

  """Metodo que obtiene un valor"""

  return  self.nombre



 def mover_a(self,x=0,y=0):

  """Metodo que cambia de valor a x y y"""

  self.x += x

  self.y += y



 def distancia(self, otra_ciudad):

  """Metodo que calcula la distancia con respecto a otra ciudad"""

  xi = pow(otra_ciudad.x-self.x,2)

  yi = pow(otra_ciudad.y-self.y,2)

  return sqrt(xi+ yi)

  

 def __del__(self):

  """Elimina la clase"""

  #obtener el nombre de una clase

  class_name = self.__class__.__name__

  print('class ', class_name, 'destroyed')





 #Inicializando un metodo estatico

 #Es el caso que no se referencia al objeto en si mismo con

 #self

 @staticmethod

 def info():

  """Metodo que devuelve el nombre del desarrollador"""

  return  "Desarrollado por Seraph"







if __name__ == "__main__":

 a = Ciudad('Valencia',5,5)

 b = Ciudad('Maracay',5,15)

 print(a)

 print(b)

 print(Ciudad.cont_ciudad)

 a.mover_a(4,3)

 b.mover_a(7,12)

 print(a.info())

 print(a)

 print(b)


El método info maneja un decorador llamado staticmethod que lo define como un método estático, ya que no se le pasa self para trabajar con métodos o variables del mismo objeto.

El resultado de ejecutar el script es el siguiente:

Ciudad: Valencia,id= 1,x= 5,y=5
Ciudad: Maracay,id= 2,x= 5,y=15
2
Desarrollado por Seraph
Ciudad: Valencia,id= 1,x= 9,y=8
Ciudad: Maracay,id= 2,x= 12,y=27
('class\t', 'Ciudad', 'destroyed')
('class\t', 'Ciudad', 'destroyed')



13 dic. 2015

Escritura y lectura de un archivo con formato bson usando Python

En artículos anteriores se ha tocado el tema de una base de datos mongodb con python, ahora en esté artículo se tocará el tema de un archivo binario que almacene documentos JSON en vez de usar un servidor mongodb.

Lo primero que se hará es crear un archivo con extensión bson donde se guardará un diccionario (json) con datos aleatorios y luego se busca la información del archivo.


Se tiene que tener instalado la libería bson para python:
apt-get install python-bson python3-bson


Escribir en un archivo bson:

#!/usr/bin/env python3

#Se importa el módulo bson

import bson

#Se abre el archivo prueba.bson para escritura y binario

f = open("prueba.bson", 'wb')

#Se intenta guardando un rango de valores del 1 al 99 decodificandolo en formato bson

try:

    for i in range(1,100):

        f.write(bson.BSON.encode({"dato":i}))

finally:

    f.close()



Al ejecutar el script se puede ver con un ls que el archivo pruebas.bson se ha creado y tiene datos dentro del mismo:
ls -l prueba.bson 
-rw-r--r-- 1 ernesto ernesto 1485 dic 13 12:16 prueba.bson


El código de lectura se muestra a continuación:
#!/usr/bin/env python3

#Se importa la libería bson

import bson

#Se abre el archivo prueba.bson en modo lectura y binario

f = open("prueba.bson", 'rb')

#Se extrae la información almacenada y se decodifica el bson

resultado = bson.decode_all(f.read())

#Se imprime en pantalla la lista

print (resultado)



El resultado de ejecutar el script se muestra a continuación:
python lectura.py 

[{u'dato': 1}, {u'dato': 2}, {u'dato': 3}, {u'dato': 4}, {u'dato': 5}, {u'dato': 6}, {u'dato': 7}, {u'dato': 8}, {u'dato': 9}, {u'dato': 10}, {u'dato': 11}, {u'dato': 12}, {u'dato': 13}, {u'dato': 14}, {u'dato': 15}, {u'dato': 16}, {u'dato': 17}, {u'dato': 18}, {u'dato': 19}, {u'dato': 20}, {u'dato': 21}, {u'dato': 22}, {u'dato': 23}, {u'dato': 24}, {u'dato': 25}, {u'dato': 26}, {u'dato': 27}, {u'dato': 28}, {u'dato': 29}, {u'dato': 30}, {u'dato': 31}, {u'dato': 32}, {u'dato': 33}, {u'dato': 34}, {u'dato': 35}, {u'dato': 36}, {u'dato': 37}, {u'dato': 38}, {u'dato': 39}, {u'dato': 40}, {u'dato': 41}, {u'dato': 42}, {u'dato': 43}, {u'dato': 44}, {u'dato': 45}, {u'dato': 46}, {u'dato': 47}, {u'dato': 48}, {u'dato': 49}, {u'dato': 50}, {u'dato': 51}, {u'dato': 52}, {u'dato': 53}, {u'dato': 54}, {u'dato': 55}, {u'dato': 56}, {u'dato': 57}, {u'dato': 58}, {u'dato': 59}, {u'dato': 60}, {u'dato': 61}, {u'dato': 62}, {u'dato': 63}, {u'dato': 64}, {u'dato': 65}, {u'dato': 66}, {u'dato': 67}, {u'dato': 68}, {u'dato': 69}, {u'dato': 70}, {u'dato': 71}, {u'dato': 72}, {u'dato': 73}, {u'dato': 74}, {u'dato': 75}, {u'dato': 76}, {u'dato': 77}, {u'dato': 78}, {u'dato': 79}, {u'dato': 80}, {u'dato': 81}, {u'dato': 82}, {u'dato': 83}, {u'dato': 84}, {u'dato': 85}, {u'dato': 86}, {u'dato': 87}, {u'dato': 88}, {u'dato': 89}, {u'dato': 90}, {u'dato': 91}, {u'dato': 92}, {u'dato': 93}, {u'dato': 94}, {u'dato': 95}, {u'dato': 96}, {u'dato': 97}, {u'dato': 98}, {u'dato': 99}]



Devuelve una lista de diccionarios(json) con los datos almacenados en el archivo.

El archivo binario contiene algo como lo siguiente: