17 años en Internet

30 julio 2014

Aprendiendo Python, día 2

1.- Objetos (II):

1.1 - Herencia:

    En Python los objetos pueden heredar sus atributos de otra clase padre.

    Ejemplo:
class item():
        # Variables de instancia
        s_name          = None
        s_description   = None
        f_value         = float(0)
        # Constructor de clase
        def __init__(self, s_name, s_description, f_value):
                self.s_name             = s_name
                self.s_description      = s_description
                self.f_value            = float(f_value) 
class potion(item):
        # Variables de instancia
        i_health_point_restores = int(0)
        # Constructor de clase
        def __init__(self, s_name, s_description, f_value, i_health_point_restores):
                self.i_health_point_restores    = int(i_health_point_restores)
                # Llamamos al constructor del padre
                item.__init__(self, s_name, s_description, f_value) 
potion_1 = potion("Pocion", "Bebida", 100, 100)
print "El item es "+ potion_1.s_name + " y recupera", potion_1.i_health_point_restores, "puntos de vida"
     Si nos fijamos, al definir una clase hija definimos quién es el padre y más tarde volvemos a referenciar el padre (otra vez) cuando hacemos llamada a su constructor. Esto se debe a que Python permite herencia múltiple.

    Ejemplo:
class age_old():
        i_age   = int(18)
        def __init__(self, i_age):
                self.i_age      = int(i_age) 
class genre():
        s_genre = "male"
        def __init__(self, s_genre):
                self.s_genre    = s_genre 
class character(age_old, genre):
        s_name  = None
        def __init__(self, s_name, i_age, s_genre):
                self.s_name     = s_name
                age_old.__init__(self, i_age)
                genre.__init__(self, s_genre)
 

character_1 = character("Messi", 24, "male")
print character_1.s_name + " has", character_1.i_age, "years old and is " + character_1.s_genre
    Otra característica de la herencia en Python, es que podemos reescribir métodos de una clase padre sin necesidad de utilizar palabras reservadas para tal efecto.

    Ejemplo:
class a():
        def to_String(self):
                return "Hola"
        pass 
class b(a):
        def to_String(self):
                return "Adios"
        pass 
v_variable = b()
print v_variable.to_String()
    Habrás notado en el ejemplo anterior que he empleado la palabra "pass". Esta palabra reservada puede emplearse en la definición de clases para ahorrarse la definición de constructores. No es recomendado su uso, pero resulta últil a la hora de codificar rápido.

2.- Bases de datos:

    Existe para Python una serie de conectores para bases de datos bastante útiles, las cuales podemos clasificar de tres tipos:

  1. Tradicionales (modelo servidor de base de datos + autenticación + cliente).
  2. Embebidas (base de datos almacenada en un fichero local).
  3. Volátiles (base de datos almacenada únicamente en la memoria RAM).

2.1 - Bases de datos tradicionales.

    Cuando hablamos de un entorno tradicional de bases de datos, nos referimos a que nuestra aplicación ataca contra un servidor o servicio de base de datos. Es decir, necesitamos conocer un usuario con el que autenticarnos, su contraseña y la ip (y/o puerto) del servicio.

    Ejemplo (requiere paquete python-mysqldb):

import MySQLdb 
# Conexion SQL
sql_connection  = MySQLdb.connect('ip_server', 'user_name', 'password', 'bda_name')
sql_cursor      = sql_connection.cursor() 
# Sentencia
sql_cursor.execute("select name from photos where 1 order by date limit 0,10") 
# Listamos los resultados
for v_tupla in sql_cursor.fetchall() :
    print v_tupla[0]

2.2.- Base de datos empotradas y volátiles

    No obstante, es bastante común en las aplicaciones de escritorio emplear bases de datos persistentes en ficheros o hacer uso de bases de datos temporales que existen en su memoria RAM. El primer modelo suele emplearse en aplicaciones que hacen uso de colecciones elevadas de datos (como por ejemplo un reproductor de audio como Amarok que gestiona y clasifica todos los ficheros por autor, género, además de almacenar el número de veces que has escuchado un tema, etcétera) mientras que el segundo suele verse más en juegos (donde se hace uso de distintas tablas y vistas que no requieren ser almacenadas para salvar la partida).

    Es cierto que una aplicación de escritorio puede emplear otro tipo de gestión de datos. Por ejemplo, hay aplicaciones que almacenan sus colecciones en ficheros de texto plano o en ficheros XML, por citar unos ejemplos comunes. No obstante, emplear una base de datos embebida o empotrada nos presenta dos mejoras importantes:
  1. Búsqueda de información más rápida, puesto que se realizan consultas SQL y no una lectura secuencial de datos.
  2. Los gestores de BDA empotradas más comunes generan ficheros cifrados.
    Tanto si queremos hacer una base de datos embebida o una volátil, en Python podemos hacer uso del plugin de SQLite3.

    Ejemplo de base de datos empotrada:

import sqlite3 
# Si el fichero de la base de datos no existe, se crea
sqlite3_connection = sqlite3.connect("mi_fichero_de_BS.db")
sqlite3_cursor = sqlite3_connection.cursor() 
# Si no existe nuestra tabla "photos", la creamos
sqlite3_cursor.execute("create table if not exists photos (id int, content blob)") 
# Leemos una imagen en formato binario
f_picture = open("test.jpg", "rb")
with f_picture:
    picture_data = f_picture.read() 
# Insertamos fotos a la BDA
sqlite3_cursor.execute("INSERT INTO photos (name, content) VALUES (0, ?)", [sqlite3.Binary(picture_data)]) 
# Confirmamos el cambio
sqlite3_connection.commit()
sqlite3_connection.close()
    Ejemplo de base de datos volátil (atención al nombre del archivo, el resto es igual que una base de datos empotrada):
import sqlite3 
# Si el fichero de la base de datos no existe, se crea
sqlite3_connection = sqlite3.connect(':memory:')
sqlite3_cursor = sqlite3_connection.cursor() 
# Si no existe nuestra tabla "empleados", la creamos
sqlite3_cursor.execute("create table if not exists employee (name varchar(200), age int)") 
# Insertamos valores
d_employees = dict()
d_employees["Sebastian Moncho"] = 30
d_employees["Garijo"] = 25
d_employees["Querido lector"] = 21
for v_index, v_value in d_employees.items():
        sqlite3_cursor.execute("INSERT INTO employee (name, age) VALUES ('"+ str(v_index) +"', " + str(v_value) +")") 
# Confirmamos el cambio
sqlite3_connection.commit() 
# Hacemos un select e imprimimos por salida estandar
sqlite3_cursor.execute("select name, age from employee where 1")
for v_tupla in sqlite3_cursor.fetchall() :
    print v_tupla[0] + " tiene", v_tupla[1] 
sqlite3_connection.close()

 

3.- Convertir a C y compilar

    Existen varios compiladores de código para Python (para aquellas ocasiones que no queramos que los demás lean nuestro código). La opción más extendida es la de hacer uso de la aplicación Cython para convertir nuestro código de Python a lenguaje C. Su uso es bien sencillo. Este código puede además compilarse y emplearse en otros sistemas (de la misma arquitectura) siempre que "linkemos" con la librería estática de Python o bien exportemos la dinámica. No voy a entrar en cómo "enlazar" las librerías, sólo me remitiré a deciros que esto existe y os voy a mostrar un ejemplo rápido.

    Convertir un código fuente .py a código fuente de c:
cython --embed sqlite3_volatil.py -o sqlite3_volatil.c
    Crear un objeto de c a partir del código fuente:
gcc -c sqlite3_volatil.c `pkg-config --cflags python` -o sqlite3_volatil.o
    Crear el ejecutable binario:
gcc sqlite3_volatil.o `pkg-config --libs python` -o sqlite3_volatil
    Si hacemos un ldd sobre nuestro nuevo binario, veremos las librerías dinámicas que emplea nuestro binario para posteriormente decidir cuales exportar:
ldd sqlite3_volatil 
linux-vdso.so.1 =>  (0x00007fff642a0000)
libpython2.7.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0 (0x00007f0301ae6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0301720000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0301501000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f03012e8000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f03010e4000)
libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007f0300ee0000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0300bda000)
/lib64/ld-linux-x86-64.so.2 (0x00007f030206c000)
    Si ejecutamos nuestro binario, veremos que tendremos el mismo comportamiento que con el intérprete:
./sqlite3_volatil
Garijo tiene 25
Querido lector tiene 21
Sebastian Moncho tiene 30
    Podemos crear un script de bash para hacer uso de las librerías dinámicas que exportemos y así poder ejecutar nuestro binario en máquinas Linux de la misma arquitectura:
#!/bin/bash
declare -r ARCH=`uname -m` 
[ "$ARCH" == "x86_64" ] &&\
        export LD_LIBRARY_PATH="./libs/x86_64" &&\
        ./sqlite3_volatil

No hay comentarios:

Publicar un comentario

Si te ha gustado la entrada o consideras que algún dato es erróneo o símplemente deseas dar algún consejo, no dudes en dejar un comentario. Todo feedback es bienvenido siempre que sea respetuoso. También puedes contactarme vía Twitter @Hamster_ruso si lo consideras necesario.