python - ¿Por qué recibo un error sobre mi clase que define__slots__ cuando intento encurtir un objeto?
pickle (3)
De la PEP 307 :
El método
__getstate__
debe devolver un valor seleccionable que represente el estado del objeto sin hacer referencia al objeto en sí. Si no existe el método__getstate__
, se usa una implementación predeterminada que devuelveself.__dict__
.
Estoy tratando de encurtir un objeto de una clase (estilo nuevo) que definí. Pero estoy recibiendo el siguiente error:
>>> with open(''temp/connection.pickle'',''w'') as f:
... pickle.dump(c,f)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/usr/lib/python2.5/pickle.py", line 1362, in dump
Pickler(file, protocol).dump(obj)
File "/usr/lib/python2.5/pickle.py", line 224, in dump
self.save(obj)
File "/usr/lib/python2.5/pickle.py", line 331, in save
self.save_reduce(obj=obj, *rv)
File "/usr/lib/python2.5/pickle.py", line 419, in save_reduce
save(state)
File "/usr/lib/python2.5/pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.5/pickle.py", line 649, in save_dict
self._batch_setitems(obj.iteritems())
File "/usr/lib/python2.5/pickle.py", line 663, in _batch_setitems
save(v)
File "/usr/lib/python2.5/pickle.py", line 306, in save
rv = reduce(self.proto)
File "/usr/lib/python2.5/copy_reg.py", line 76, in _reduce_ex
raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
No __slots__
explícitamente __slots__
en mi clase. ¿Algo que hice lo definí implícitamente? ¿Cómo trabajo alrededor de esto? ¿Necesito definir __getstate__
?
Actualización: gnibbler eligió un buen ejemplo. La clase del objeto que estoy tratando de encurtir envuelve un socket. (Se me ocurre ahora que) los sockets definen __slots__
y no __getstate__
por una buena razón. Supongo que una vez que finaliza un proceso, otro proceso no puede deshacerse y usar la conexión de socket del proceso anterior. Entonces, si bien estoy aceptando la excelente respuesta de Alex Martelli , tendré que seguir una estrategia diferente a la de "compartir" la referencia del objeto.
La clase que define __slots__
(y no __getstate__
) puede ser una clase ancestral tuya, o una clase (o clase antecesora) de un atributo o elemento tuyo, directa o indirectamente: esencialmente, la clase de cualquier objeto en el gráfico dirigido de referencias con su objeto como raíz, ya que el decapado debe guardar el gráfico completo.
Una solución simple para su dilema es usar el protocolo -1
, que significa "el mejor protocolo que puede usar"; el valor predeterminado es un antiguo protocolo basado en ASCII que impone esta limitación sobre __slots__
vs __getstate__
. Considerar:
>>> class sic(object):
... __slots__ = ''a'', ''b''
...
>>> import pickle
>>> pickle.dumps(sic(), -1)
''/x80/x02c__main__/nsic/nq/x00)/x81q/x01.''
>>> pickle.dumps(sic())
Traceback (most recent call last):
[snip snip]
raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
>>>
Como ve, el protocolo -1
toma las __slots__
con calma, mientras que el protocolo predeterminado le da la misma excepción que vio.
Los problemas con el protocolo -1
: produce una cadena / archivo binario, en lugar de uno ASCII como el protocolo predeterminado; el archivo decapado resultante no se podría cargar con versiones suficientemente antiguas de Python. Las ventajas, además de la clave one wrt __slots__
, incluyen resultados más compactos y mejor rendimiento.
Si se ve obligado a usar el protocolo predeterminado, deberá identificar exactamente qué clase le está causando problemas y exactamente por qué. Podemos discutir estrategias si este es el caso (pero si es posible que pueda usar el protocolo -1
, eso es mucho mejor que no vale la pena discutirlo ;-) y la simple inspección de código en busca de la clase problemática / objeto está resultando demasiado complicada (I tenga en mente algunos trucos basados en copia profunda para obtener una representación útil de todo el gráfico, en caso de que se lo pregunte).
Quizás un atributo de tu instancia esté usando __slots__
Por ejemplo, socket
tiene __slots__
por lo que no puede ser decapado
__getstate__
identificar qué atributo está causando el error y escribir su propio __getstate__
y __setstate__
para ignorar ese atributo