Serializador recursivo para Python

Un serializador es un algoritmo capaz de convertir objetos, en este caso de Python, en cadenas de bits.

Para cierto proyecto en curso, me ha hecho falta serializar objetos complejos para guardarlos en cookies del navegador, y en este caso Pickle no es una opción, ya que es inseguro y permitiría ejecutar código arbitrario en el servidor a cualquiera que se le ocurriera modificar dicha cookie.

Existe otro serializador, Cerealizer, que permite elegir qué clases son las que permitimos coger del string serializado, además de los tipos básicos del lenguaje, pero alguna razón no me llegó a funcionar. De modo que he tenido que perder una semana de mi vida para crear mi propio algoritmo, recursivo, para tal menester.

Es bastante seguro, ya que obliga registrar todas las clases que puedan ser serializadas, y dichas clases deben definir sus propios métodos, __getstate__ y __setstate__, opcionales en Pickle, que definen qué datos son realmente serializados y cómo se deben recoger dichos datos por la clase cuando se carguen.

La cadena generada contiene únicamente caracteres imprimibles (a menos que alguna cadena serializada contenga otra cosa), escapa las cadenas unicode, es compacta (más que las cadenas de Pickle en sus protocolos 0 y 1, pero menos que su protocolo 2), tienen mejor ratio de compresión y el algoritmo en general es un 35% mas rápido que el mismo Pickle (cPickle sería mucho mas rápido, pero no está escrito en Python, sino en C).

Podéis bajarlo (14.1KiB) de las descargas del blog en Google Code.

Usarlo es muy sencillo

from PRSerializer import register, loads, dumps

class Clase(object):
atributo1 = True
atributo2 = None

def __init__(self):
self.atributo2 = xrange(1000)
# El serializador reconoce xrange nativamente
# por lo que es preferible usarlo frente a range

def __getstate__(self):
# Requerido. Retorna lo que realmente será serializado
return (self.atributo1, self.atributo2)

def __setstate__(self,o):
# Requerido. Recoge y utiliza los datos serializados
self.atributo1 = o[0]
self.atributo2 = o[1]

# Registramos la clase para el serializador
register(Clase)

# Creamos una instancia de la clase y trabajamos con ella normalmente
instancia = Clase()
instancia.atributo1 = "Cadena de texto"

# Serializamos la instancia
a = dumps(instancia)
print "Cadena:",a

# Deserializamos la instancia
b = loads(a)
print "Instancia:",repr(b)

Os dejo los resultados de una prueba de rendimiento, incluida en el módulo, en un dual core de 2.80Ghz.

Performance test: serializing, unserializing, zlib compression and pickle
1 dict
2000 lists (1000 are empty)
100000 integers (1000 are dictionary keys)
100000 strings
51000 objects
100000 booleans

Serializing time: 5.641000s ( 5463337 bytes )
Unserializing time: 3.328000s ( 1641627.670456 B/s )
Zlib compress time: 0.140000s ( 26580 bytes )

Pickling time: 9.703000s ( 5268354 bytes )
Unpickling time: 4.312000s ( 1221788.950771 B/s )
Zlib compress time: 1.344000s ( 506233 bytes )

0 comentarios:

Publicar un comentario