Python: Serialización de objetos

Algunas veces tenemos la necesidad de guardar un objeto a disco para poder recuperarlo más tarde, o puede que nos sea necesario mandar un objeto a través de la red, a otro programa en Python ejecutándose en otra máquina.

Al proceso de transformar el estado de un objeto en un formato que se pueda almacenar, recuperar y transportar se le conoce con el nombre de serialización o marshalling.

En Python tenemos varios módulos que nos facilitan esta tarea, como marshal, pickle, cPickle y shelve.

El módulo marshal es el más básico y el más primitivo de los tres, y es que, de hecho, su propósito principal y su razón de ser no es el de serializar objetos, sino trabajar con bytecode Python (archivos .pyc).

marshal sólo permite serializar objetos simples (la mayoría de los tipos incluidos por defecto en Python), y no proporciona ningún tipo de mecanismo de seguridad ni comprobaciones frente a datos corruptos o mal formateados. Es más, el formato utilizado para guardar el bytecode (y por tanto el formato utilizado para guardar los objetos con marshal) puede cambiar entre versiones, por lo que no es adecuado para almacenar datos de larga duración.

pickle, por su parte, permite serializar casi cualquier objeto (objetos de tipos definidos por el usuario, colecciones que contienen colecciones, etc) y cuenta con algunos mecanismos de seguridad básicos. Sin embargo, al ser más complejo que marshal, y, sobre todo, al estar escrito en Python en lugar de en C, como marshal, también es mucho más lento.

La solución, si la velocidad de la serialización es importante para nuestra aplicación, es utilizar cPickle, que no es más que es una implementación en C de pickle. cPickle es hasta 1000 veces más rápido que pickle, y prácticamente igual de rápido que marshal.

Si intentamos importar cPickle y se produce un error por algún motivo, se lanzará una excepción de tipo ImportError. Para utilizar cPickle si está disponible y pickle en caso contrario, podríamos usar un código similar al siguiente:

try:
    import cPickle as pickle
except ImportError:
    import pickle

as en un import sirve para importar el elemento seleccionado utilizando otro nombre indicado, en lugar de su nombre.

La forma más sencilla de serializar un objeto usando pickle es mediante una llamada a la función dump pasando como argumento el nombre del objeto a serializar y un objeto archivo en el que guardarlo (o cualquier otro tipo de objeto similar a un archivo, siempre que ofrezca métodos read, realine y write).

try:
    import cPickle as pickle
except ImportError:
    import pickle

fichero = file("datos.dat", "w")
animales = ["piton", "mono", "camello"]

pickle.dump(animales, fichero)

fichero.close()

La función dump también tiene un parámetro opcional protocol que indica el protocolo a utilizar al guardar. Por defecto su valor es 0, que utiliza formato texto y es el menos eficiente. El protocolo 1 es más eficiente que el 0, pero menos que el 2. Tanto el protocolo 1 como el 2 utilizan un formato binario para guardar los datos.

try:
    import cPickle as pickle
except ImportError:
    import pickle

fichero = file("datos.dat", "w")
animales = ["piton", "mono", "camello"]

pickle.dump(animales, fichero, 2)

fichero.close()

Para volver a cargar un objeto serializado se utiliza la función load, a la que se le pasa el archivo en el que se guardó.

try:
    import cPickle as pickle
except ImportError:
    import pickle

fichero = file("datos.dat", "w")
animales = ["piton", "mono", "camello"]

pickle.dump(animales, fichero)

fichero.close()

fichero = file("datos.dat")

animales2 = pickle.load(fichero)
print animales2

Supongamos ahora que queremos almacenar un par de listas en un fichero. Esto sería tan sencillo como llamar una vez a dump por cada lista, y llamar después una vez a load por cada lista.

fichero = file("datos.dat", "w")
animales = ["piton", "mono", "camello"]
lenguajes = ["python", "mono", "perl"]

pickle.dump(animales, fichero)
pickle.dump(lenguajes, fichero)

fichero = file("datos.dat")

animales2 = pickle.load(fichero)
lenguajes2 = pickle.load(fichero)
print animales2
print lenguajes2

Pero, ¿y si hubiéramos guardado 30 objetos y quisiéramos acceder al último de ellos? ¿o si no recordáramos en qué posición lo habíamos guardado? El módulo shelve extiende pickle / cPickle para proporcionar una forma de realizar la serialización más clara y sencilla, en la que podemos acceder a la versión serializada de un objeto mediante una cadena asociada, a través de una estructura parecida a un diccionario.

La única función que necesitamos conocer del módulo shelve es open, que cuenta con un parámetro filename mediante el que indicar la ruta a un archivo en el que guardar los objetos (en realidad se puede crear más de un archivo, con nombres basados en filename, pero esto es transparente al usuario).

La función open también cuenta con un parámetro opcional protocol, con el que especificar el protocolo que queremos que utilice pickle por debajo.

Como resultado de la llamada a open obtenemos un objeto Shelf, con el que podemos trabajar como si de un diccionario normal se tratase (a excepción de que las claves sólo pueden ser cadenas) para almacenar y recuperar nuestros objetos.

Como un diccionaro cualquiera la clase Shelf cuenta con métodos get, has_key, items, keys, values, …

Una vez hemos terminado de trabajar con el objeto Shelf, lo cerramos usando el método close.

import shelve

animales = ["piton", "mono", "camello"]
lenguajes = ["python", "mono", "perl"]

shelf = shelve.open("datos.dat")
shelf["primera"] = animales
shelf["segunda"] = lenguajes

print shelf["segunda"]

shelf.close()


Comentarios
  1. Fox

    Está bien el tuto si, yo he usado pickle alguna vez que otra con cosillas simples, pero no conocia el tercer parametro de dump jeje. De shelve si conocia su existencia, pero no me habia parado a leer que tenia de diferente con pickle.

    Un saludo.

    Responder

  2. Bakura12

    Mmm… Hablando de Python xD

    Pondras “Python para todos” en Bubok?
    Yo lo quiero comprar xD

    Responder

  3. guillem

    Muchas gracias por tu aporte!
    Realmente necesitaba un sitio así, con entradas detalladas sobre python cada cierto tiempo.
    Sigue así!

    Responder

  4. genial pero fuera delos procesos y utilerias de python

    alguien sabe como crear en si el programa en mac ???

    con appletbuilder?

    Responder

  5. Muy bueno el artículo. En tu linea 🙂

    Responder

  6. Joaquin

    Holas! Estoy leyendo tu libro tutorial que lo imprimí ayer. Esta muy bien y me está gustando mucho. Pero tengo una duda. Que IDE se puede utilizar para python? Sobre todo alguna que ayude mucho para el desarrollo de sitios web con Django.
    He googleado algo y me he encontrado con OpenKomodo y con Stanis Python Editor. Tienen buena pinta pero ninguna trae soporte para proyectos de Django.
    Gracias, Joaquín

    Responder

  7. Hideo
  8. usuario

    Dejen de copiar del libro Python para todos …
    Eso se llama Plagio!

    Responder

  9. Anónimo

    dejenme libros de python donde se ha resumido

    Responder

  10. hola, soy miguel, programador ciego de argentina. estoy iniciandome con python, la verdad tengo que agradecerte por tu manual, muy claro. muchas gracias por tu tiempo dedicado!.

    Responder

  11. Marco Castro

    Que buen articulo.
    Gracias.

    Responder

  12. Es un muy bien artículo les recomiendo me ayudo en mucho gracias. 🙂

    Responder

  13. Ademar

    Muito bom!!!

    só deixo uma dica, para serializar imagens você vai precisar de algo como: http://stackoverflow.com/questions/10118068/pickleable-image-object

    Responder

  14. Carlos

    Hola que tal, soy nuevo en Python, tengo unos archivos .DAT generados en Python mediante un sistema, puedo visualizar parcialmente la informacion con btfiler, y por lo que he leido usaron tal vez shelve o pickle para crearlo, no tengo la estructura, hay forma de recuperar la informacion con shelve sin saber que objetos o estructura contienen los archivos? Muchas gracias.

    Responder

  15. Anónimo

    Hola amigo que tal, intento implementar tu codigo pero me sale un error con “file” me dice que no es algo reconocido

    Responder

Deja un comentario