Ver índice de contenidos del libro

11.7. Persistencia de datos

Se llama persistencia a la capacidad de guardar la información de un programa para poder volver a utilizarla en otro momento. Es lo que los usuarios conocen como Guardar el archivo y después Abrir el archivo. Pero para un programador puede significar más cosas y suele involucrar un proceso de serialización de los datos a un archivo o a una base de datos o a algún otro medio similar, y el proceso inverso de recuperar los datos a partir de la información serializada.

Por ejemplo, supongamos que en el desarrollo de un juego se quiere guardar en un archivo la información referente a los ganadores, el puntaje máximo obtenido y el tiempo de juego en el que obtuvieron ese puntaje.

En el juego, esa información podría estar almacenada en una lista de tuplas:

[(nombre1, puntaje1, tiempo1), (nombre2, puntaje2, tiempo2), ...]

Esta información se puede guardar en un archivo de muchas formas distintas. En este caso, para facilitar la lectura del archivo de puntajes para los humanos, se decide guardarlos en un archivo de texto, donde cada tupla ocupará una línea y los valores de las tuplas estarán separados por comas.

En el código 11.6 se muestra un módulo capaz de guardar y recuperar los puntajes en el formato especificado.

Dadas las especificaciones del problema al guardar los valores en el archivo, es necesario convertir el puntaje (que es un valor numérico) en una cadena, y al abrir el archivo es necesario convertirlo nuevamente en un valor numérico.

Nota Es importante notar que tanto la función que almacena los datos como la que los recupera requieren que la información se encuentre de una forma determinada y de no ser así, fallarán. Es por eso que estas condiciones se indican en la documentación de las funciones como sus precondiciones. En próximas unidades veremos cómo evitar que falle una función si alguna de sus condiciones no se cumple.

Es bastate sencillo probar el módulo programado y ver que lo que se guarda es igual que lo que se recupera:

# Código 11.6: puntajes.py: Módulo para guardar y recuperar puntajes en un archivo
 
#! /usr/bin/env python
# encoding: latin1
 
def guardar_puntajes(nombre_archivo, puntajes):
    """ Guarda la lista de puntajes en el archivo.
    Pre: nombre_archivo corresponde a un archivo válido,
         puntajes corresponde a una lista de tuplas de 3 elementos.
    Post: se guardaron los valores en el archivo,
          separados por comas.
    """
 
    archivo = open(nombre_archivo, "w")
    for nombre, puntaje, tiempo in puntajes:
        archivo.write(nombre+","+str(puntaje)+","+tiempo+"\\n")
    archivo.close()
 
def recuperar_puntajes(nombre_archivo):
    """ Recupera los puntajes a partir del archivo provisto.
        Devuelve una lista con los valores de los puntajes.
    Pre: el archivo contiene los puntajes en el formato esperado,
         separados por comas
    Post: la lista devuelta contiene los puntajes en el formato:
          [(nombre1,puntaje1,tiempo1),(nombre2,puntaje2,tiempo2)].
    """
 
    puntajes = []
    archivo = open(nombre_archivo, "r")
    for línea in archivo:
        nombre, puntaje, tiempo = línea.rstrip("\\n").split(",")
        puntajes.append((nombre,int(puntaje),tiempo))
    archivo.close()
    return puntajes
>>> import puntajes
>>> valores = [("Pepe", 108, "4:16"), ("Juana", 2315, "8:42")]
>>> puntajes.guardar_puntajes("puntajes.txt", valores)
>>> recuperado = puntajes.recuperar_puntajes("puntajes.txt")
>>> print recuperado
[('Pepe', 108, '4:16'), ('Juana', 2315, '8:42')]

Guardar el estado de un programa se puede hacer tanto en un archivo de texto, como en un archivo binario. En muchas situaciones es preferible guardar la información en un archivo de texto, ya que de esta manera es posible modificarlo fácilmente desde cualquier editor de textos.

En general, los archivos de texto van a desperdiciar un poco más de espacio, pero son más faciles de entender y fáciles de usar desde cualquier programa.

Por otro lado, en un archivo binario bien definido se puede evitar el desperdicio de espacio, o también hacer que sea más rápido acceder a los datos. Además, para ciertas aplicaciones como archivos de sonido o video, tendría poco sentido almacenarlos en archivos de texto.

En definitiva, la decisión de qué formato usar queda a discreción del programador. Es im-portante recordar que el sentido común es el valor más preciado en un programador.

11.7.1. Persistencia en archivos CSV

Un formato que suele usarse para transferir datos entre programas es CSV (del inglés comma separated values: valores separados por comas) es un formato bastante sencillo, tanto para leerlo como para procesarlo desde el código, se parece al formato visto en el ejemplo anteriormente.

Nombre,Apellido,Telefono,Cumpleaños
"John","Smith","555-0101","1973-11-24"
"Jane","Smith","555-0101","1975-06-12"

En el ejemplo se puede ver una pequeña base de datos. En la primera línea del archivo tenemos los nombres de los campos, un dato opcional desde el punto de vista del procesamiento de la información, pero que facilita entender el archivo.

En las siguientes líneas se ingresan los datos de la base de datos, cada campo separado por comas. Los campos que son cadenas se suelen escribir entre comillas dobles, si alguna cadena contiene alguna comilla doble se la reemplaza por \" y una contrabarra se escribe como \\.

En Python es bastante sencillo procesar de este tipo de archivos, tanto para la lectura como para la escritura, mediante el módulo csv que ya se encuentra preparado para eso.

La funciones del ejemplo anterior podría programarse mediante el módulo csv. En el Código 11.7 se muestra una posible implementación que utiliza este módulo.

Si se prueba este código, se obtiene un resultado idéntico al obtenido anteriormente:

>>> import puntajes_csv                                                     
>>> valores = [("Pepe", 108, "4:16"), ("Juana", 2315, "8:42")]
>>> puntajes_csv.guardar_puntajes("puntajes.txt", valores)
>>> recuperado = puntajes_csv.recuperar_puntajes("puntajes.txt")
>>> print recuperado
[('Pepe', 108, '4:16'), ('Juana', 2315, '8:42')]

El código, en este caso, es muy similar, ya que en el ejemplo original se hacían muy pocas consideraciones al respecto de los valores: se asumía que el primero y el tercero eran cadenas mientras que el segundo necesitaba ser convertido a cadena.

# Código 11.7: puntajes_csv.py: Módulo para guardar y recuperar puntajes en un archivo que usa csv
 
#! /usr/bin/env python
# encoding: latin1
 
import csv
 
def guardar_puntajes(nombre_archivo, puntajes):
    """ Guarda la lista de puntajes en el archivo.
    Pre: nombre_archivo corresponde a un archivo válido,
         puntajes corresponde a una lista de secuencias de elementos.
    Post: se guardaron los valores en el archivo,
          separados por comas.
    """
 
    archivo = open(nombre_archivo, "w")
    archivo_csv = csv.writer(archivo)
    archivo_csv.writerows(puntajes)
    archivo.close()
 
def recuperar_puntajes(nombre_archivo):
    """ Recupera los puntajes a partir del archivo provisto.
        Devuelve una lista con los valores de los puntajes.
    Pre: el archivo contiene los puntajes en el formato esperado,
         separados por comas
    Post: la lista devuelta contiene los puntajes en el formato:
          [(nombre1,puntaje1,tiempo1),(nombre2,puntaje2,tiempo2)].
"""
 
puntajes = []
archivo = open(nombre_archivo, "r")
archivo_csv = csv.reader(archivo)
for nombre, puntaje, tiempo in archivo_csv:
    puntajes.append((nombre, int(puntaje), tiempo))
archivo.close()
return puntajes

Nota Es importante notar, entonces, que al utilizar el módulo csv en lugar de hacer el procesamiento en forma manual, se obtiene un comportamiento más robusto, ya que el módulo csv tiene en cuenta muchos más casos que nuestro código original no. Por ejemplo, el código anterior no tenía en cuenta que el nombre pudiera contener una coma.

En el apéndice de esta unidad puede verse una aplicación completa de una agenda, que almacena los datos del programa en archivos csv.

11.7.2. Persistencia en archivos binarios

En el caso de que decidiéramos grabar los datos en un archivo binario, Python incluye una herramienta llamada pickle que permite hacerlo de forma muy sencilla. Hay que tener en cuenta, sin embargo, que no es nada simple acceder a un archivo en este formato desde un programa que no esté escrito en Python.

En el Código 11.8 se muestra el mismo ejemplo de almacenamiento de puntajes, utilizando el módulo pickle.

# Código 11.8: puntajes_pickle.py: Módulo para guardar y recuperar puntajes en un archivo que usa pickle
 
#! /usr/bin/env python
# encoding: latin1
 
import pickle
 
def guardar_puntajes(nombre_archivo, puntajes):
    """ Guarda la lista de puntajes en el archivo.
    Pre: nombre_archivo corresponde a un archivo válido,
         puntajes corresponde a los valores a guardar
    Post: se guardaron los valores en el archivo en formato pickle.
    """
 
    archivo = open(nombre_archivo, "w")
    pickle.dump(puntajes, archivo)
    archivo.close()
 
def recuperar_puntajes(nombre_archivo):
    """ Recupera los puntajes a partir del archivo provisto.
        Devuelve una lista con los valores de los puntajes.
    Pre: el archivo contiene los puntajes en formato pickle
    Post: la lista devuelta contiene los puntajes en el
          mismo formato que se los almacenó.
    """
 
    archivo = open(nombre_archivo, "r")
    puntajes = pickle.load(archivo)
    archivo.close()
    return puntajes

El funcionamiento de este programa será idéntico a los anteriores. Pero el archivo generado será muy distinto a los archivos generados anteriormente. En lugar de ser un archivo de fácil lectura, tendrá la forma:

(lp0
(S'Pepe'
p1
I108
S'4:16'
p2
tp3
a(S'Juana'
p4
I2315
S'8:42'
p5
tp6

En el apéndice de esta unidad puede verse una aplicación completa de una agenda, que utiliza pickle para almacenar datos en archivos.

Copyright (c) 2011-2014 Rosita Wachenchauzer, Margarita Manterola, Maximiliano Curia, Marcos Medrano, Nicolás Paez. La copia y redistribución de esta página se permite bajo los términos de la licencia Creative Commons Atribución - Compartir Obras Derivadas Igual 3.0 siempre que se conserve esta nota de copyright.