Serializando datos nativos en Python

Resulta que para un proyecto en Python en el que estoy trabajando necesitaba serializar algunas estructuras. El tema es que únicamente hacía falta serializar tipos nativos como listas, diccionarios, cadenas, enteros, etc…

Lo que me hacía falta era:

  • representación compacta
  • serialización/deserialización rápida
  • legible / editable por un humano

Así que después de pensar un rato, he encontrado 3 posibles soluciones para serializar datos arbitrarios:

  • pickle/cPickle que serializa cualquier cosa, viene con python, pero no es legible/editable
  • simplejson que serializa en formato JSON (el resultado es legible y editable)
  • PyYAML que serializa en YAML (el resultado es legible y editable)

El siguiente paso era coger un buen montón de datos, y hacer tests para ver que tal funcionan los encoders/decoders en cada uno de esos módulos.

A continuación puedes ver los gráficos de los tiempos de encoding/decoding para dos conjuntos de datos empleando cada uno de los formatos anteriores:

Python Serialization Speed Test (Tiny Dataset)

Python Serialization Speed Test (Huge Dataset)

A parte de la velocidad, también me interesaba medir la longitud de las cadenas generadas (cuanto menos, mejor):

  • yaml   tiny = 32604  // huge = 912912
  • json   tiny = 31305 // huge = 876540
  • pickle-raw    tiny = 34504 // huge = 986531
  • pickle-highest   tiny = 37541 // huge = 1101480
Conclusión:

Como se puede ver en las gráficas, y en la tabla anterior, considero que JSON es el que me hace mejor papel. Codifica / decodifica ligeramente más despacio que pickle (un 4% más despacio) pero la cadena resultante es legible y editable y además ocupa menos espacio que picke y que yaml.

Todos los tests han sido realizados con Python 2.5

El código:

Finalmente, aquí dejo un fragmento de código usado para obtener los tiempos que aparecen en los gráficos anteriores:

start = time.time()
for i in range (0, 100):
yaml.safe_load (yaml.safe_dump (data))
self.write ("YAML TIME = %.2f
" % (time.time() - start))
 
start = time.time()
for i in range (0, 100):
JSONDecoder().decode (JSONEncoder().encode (data))
self.write ("JSON TIME = %.2f
" % (time.time() - start))
 
start = time.time()
for i in range (0, 100):
pickle.loads (pickle.dumps (data, pickle.HIGHEST_PROTOCOL))
self.write ("PICKLE TIME = %.2f
" % (time.time() - start))
 
start = time.time()
for i in range (0, 100):
pickle.loads (pickle.dumps (data, 0))
self.write ("PICKLE-RAW TIME = %.2f
" % (time.time() - start))

Trackback URL

, , , , ,

  1. Juanjo
    21/09/2010 at 3:26 am Permalink

    Yo además estuve comparando implementaciones de JSON, y al menos en Python 2.6, la que trae de casa (simplejson?) es la más rápida.

    Bueno, en realidad está bastante claro cuando ves que «el resto» de implementaciones están medio abandonadas (o eso, o no busqué bien :D).

  2. Pau Sanchez
    21/09/2010 at 4:20 am Permalink

    Yo hace algún tiempo también me puse a buscar implementaciones de json, y al final simplejson es la que uso desde entonces.

  3. David A
    28/06/2011 at 7:35 pm Permalink

    Otra opción es serializar con repr() y de-serializar con eval(). Mi pruebita sugiere es dos veces más rápidamente que pickle, por lo menos.

    start = time.time()
    for i in range (0, 100):
    eval(repr(data))
    print («REPR/EVAL TIME = %.2f\n» % (time.time() – start))

    Por supuesto, este método tiene sus desventajas. Desemejante de yaml, json y pickle, la representación es específico de python. No para la comunicación con programas non-python. Es también inseguro eval() a los datos extraños.