Pickle todos los atributos excepto uno

¿Cuál es la mejor manera de escribir un método __getstate__ que __getstate__ casi todos los atributos de un objeto, pero excluye algunos?

Tengo un objeto con muchas propiedades, incluyendo una que hace referencia a un método de instancia. Los instancemethod’s no son pickleable, por lo que recibo un error cuando bash pickle este objeto:

 class Foo(object): def __init__(self): self.a = 'spam' self.b = 'eggs' self.c = 42 self.fn = self.my_func def my_func(self): print 'My hovercraft is full of eels' import pickle pickle.dumps(Foo()) # throws a "can't pickle instancemethod objects" TypeError 

Este método __getstate__ corrige esto, pero luego tengo que incluir manualmente todas las propiedades que quiero serializar:

 def __getstate__(self): return { 'a': self.a, 'b': self.b, 'c': self.c } 

Eso no es muy escalable ni mantenible si tengo un objeto con muchos atributos o que cambia con frecuencia.

La única alternativa que se me ocurre es algún tipo de función auxiliar que recorre las propiedades de un objeto y las agregue (o no) al diccionario, según el tipo.

La única alternativa que se me ocurre es algún tipo de función auxiliar que recorre las propiedades de un objeto y las agregue (o no) al diccionario, según el tipo.

Sí, creo que eso es más o menos lo que te queda, si quieres suficiente “magia” para permitirte ser perezoso (y / o permitir atributos agregados dinámicamente). Tenga en cuenta que ” pickle no puede manejar esto” no es la única razón por la que no quiera incluir algo en el estado pickled.

Pero no es tan difícil como parece pensar, asumiendo que tienes un código para el “¿Debo declinar esto?” lógica:

 def __getstate__(self): return dict((k, v) for (k, v) in self.__dict__.iteritems() if should_pickle(v)) 

Usando is_instance_method de una respuesta anterior:

 def __getstate__(self): return dict((k, v) for k, v in self.__dict__.iteritems() if not is_instance_method(getattr(self, k))) 

Aunque la operación is_instance_method también se puede realizar de forma menos “mágica” tomando un método de instancia conocido, digamos my_func , y tomando su tipo.

 def __getstate__(self): instancemethod = type(self.my_func) return dict((k, v) for k, v in self.__dict__.iteritems() if not isinstance(getattr(self, k), instancemethod)) 

Siempre puedes simplemente quitar los elementos malos:

 def __getstate__(self): state = self.__dict__ del state[...] return state 

Corté la raíz de su problema e intenté serializar los elementos llamados ‘no seleccionables’ primero. Para hacer esto, usaría dill , que puede serializar casi cualquier cosa en python. Dill también tiene algunas buenas herramientas para ayudarlo a comprender qué está causando que su decapado falle cuando falla su código.

 >>> import dill >>> dill.loads(dill.dumps(your_bad_object)) >>> ... >>> # if you get a pickling error, use dill's tools to figure out a workaround >>> dill.detect.badobjects(your_bad_object, depth=0) >>> dill.detect.badobjects(your_bad_object, depth=1) >>> ... 

Si lo desea, puede usar los badobjects de badobjects (o una de las otras funciones de detección) para sumergirse recursivamente en la cadena de referencia de su objeto y sacar los objetos que no se pueden recoger, en lugar de llamarlos a todas las profundidades, como se mencionó anteriormente.

solución __slots__

Si está utilizando ranuras, puede evitar la repetición de miembros para excluir con:

 class C(object): _pickle_slots = ['i'] __slots__ = _pickle_slots + ['j'] def __init__(self, i, j): self.i = i self.j = j def __getstate__(self): return (None, {k:getattr(self, k) for k in C._pickle_slots }) o = pickle.loads(pickle.dumps(C(1, 2), -1)) # i is there assert oi == 1 # j was excluded try: oj except: pass else: raise 

Probado en Python 2.7.6.

Para su caso específico (evitando que una función sea decapada), use esto:

self.__class__.fn = self.__class__.my_func

Ahora, en lugar de agregar una función a una instancia de una clase, la ha agregado a la clase en sí misma, por lo tanto, la función no se eliminará. Esto no funcionará si desea que cada instancia tenga su propia versión de fn .

Mi escenario era que quería agregar selectivamente get_absolute_url a algunos modelos de Django, y quería definir esto en una clase de BaseModel abstracta. Tuve self.get_absolute_url = … y me encontré con el problema de los pickle . Acabo de agregar __class__ a la tarea resuelta el problema en mi caso.