2012-04-28 11 views
6

Después de leer la documentación salmuera, tengo la impresión de que una clase debe implementar ya sea __reduce__ o __getstate__ para obtener escabeche correctamente. Pero, ¿cómo funciona el decapado de diccionarios? No tienen ninguno de esos atributos:¿Cómo se encurten los objetos dict?

> dict(a=1).__reduce__() 

--------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 
/home/daniyar/work/Apr24/<ipython-input-30-bc1cbd43305b> in <module>() 
----> 1 dict(a=1).__reduce__() 

/usr/lib/python2.6/copy_reg.pyc in _reduce_ex(self, proto) 
    68  else: 
    69   if base is self.__class__: 
---> 70    raise TypeError, "can't pickle %s objects" % base.__name__ 
    71   state = base(self) 
    72  args = (self.__class__, base, state) 

TypeError: can't pickle dict objects 



> dict(a=1).__getstate__() 

--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
/home/daniyar/work/Apr24/<ipython-input-31-00932fb40067> in <module>() 
----> 1 dict(a=1).__getstate__() 

AttributeError: 'dict' object has no attribute '__getstate__' 

Además, ¿cómo se decaparon las clases derivadas de dict?

Respuesta

3

Los métodos __reduce__ y __getstate__ están destinados a ser el límite inferior de los métodos de decapado, para que implemente en sus clases personalizadas cuando necesita algún trato especial por parte del intérprete.

Por ejemplo, si una instancia de una clase de extensión está dentro de un diccionario que está tratando de agrupar, eso hace que todo el diccionario sea desaplicable si su clase no implementa esos métodos que dicen cómo desenterrarlo.

El intérprete sabe a Pickle órdenes internas, y en Pickle un diccionario que puedes usar pickle.dump o pickle.dumps método, no llamando __reduce__ o __getstate__.

3

Decapado no requiere ni __reduce__ o __getstate__. Esos son métodos que puede usar para controlar el decapado, pero pickle funcionará perfectamente en los tipos integrados.

1

La respuesta útil que recibí de here

Esto es lo que se supone que es el interior __getstate__ y __setstate__. A pesar de que de alguna manera no se puede utilizar de inmediato, ya que supone que, pero se puede hacer a partir de cero, de esta manera:

def __getstate__(self): 
    result = self.__dict__.copy() 
    return result 

def __setstate__(self, dict): 
    self.__dict__ = dict 
1

Todas las buenas respuestas, pero ignorar la pregunta:

Además, ¿cómo se decaparon las clases derivadas de dict?

Son encurtidos por referencia, como cualquier otra clase. Si miras el pickle, puedes ver qué está haciendo python.

>>> class MyDict(dict): 
... def __repr__(self): 
...  return "MyDict({})".format(dict(i for i in self.items())) 
... 
>>> m = MyDict(a=1,b=2) 
>>> m 
MyDict({'a': 1, 'b': 2}) 
>>> import pickle 
>>> # reconstructor called on class MyDict that lives in __main__ 
>>> # and contains a __builtin__ dict with contents ('a' and 'b') 
>>> pickle.dumps(m) 
"ccopy_reg\n_reconstructor\np0\n(c__main__\nMyDict\np1\nc__builtin__\ndict\np2\n(dp3\nS'a'\np4\nI1\nsS'b'\np5\nI2\nstp6\nRp7\n." 
>>> m.clear() 
>>> # removing the contents, to show how that affects the pickle 
>>> pickle.dumps(m) 
'ccopy_reg\n_reconstructor\np0\n(c__main__\nMyDict\np1\nc__builtin__\ndict\np2\n(dp3\ntp4\nRp5\n.' 
>>> # now, just looking at the class itself, you can see it's by reference 
>>> pickle.dumps(MyDict) 
'c__main__\nMyDict\np0\n.' 

O, podríamos hacer lo mismo, pero inspeccionar los encurtidos desmontados. Puede ver exactamente qué instrucciones están almacenadas.

>>> pickletools.dis(pickle.dumps(m)) 
    0: c GLOBAL  'copy_reg _reconstructor' 
    25: p PUT  0 
    28: ( MARK 
    29: c  GLOBAL  '__main__ MyDict' 
    46: p  PUT  1 
    49: c  GLOBAL  '__builtin__ dict' 
    67: p  PUT  2 
    70: (  MARK 
    71: d   DICT  (MARK at 70) 
    72: p  PUT  3 
    75: t  TUPLE  (MARK at 28) 
    76: p PUT  4 
    79: R REDUCE 
    80: p PUT  5 
    83: . STOP 
highest protocol among opcodes = 0 
>>> pickletools.dis(pickle.dumps(MyDict)) 
    0: c GLOBAL  '__main__ MyDict' 
    17: p PUT  0 
    20: . STOP 
highest protocol among opcodes = 0 

La clase se almacena definitivamente por referencia, incluso pensó que se deriva de una dict en lugar de object. La referencia es al nombre, lo que significa que una vez que la sesión __main__ está cerrado, se perdería la definición de clase y la salmuera que depende de MyClass no se puede cargar.

Ahora, vamos a ver el dict.A dict pickles confiando primero en python sabiendo cómo serializar objetos fundamentales como el dict (como se menciona en otras respuestas), luego se trata de serializar los contenidos. Puede ver que tiene dos strings, que python también sabe cómo serializar.

Esto significa que si tiene objetos no serializables en el dict, fallará.

>>> d['c'] = MyDict.__repr__ 
>>> d 
{'a': 1, 'c': <unbound method MyDict.__repr__>, 'b': 2} 
>>> pickle.dumps(d) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1374, in dumps 
    Pickler(file, protocol).dump(obj) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump 
    self.save(obj) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save 
    f(self, obj) # Call unbound method with explicit self 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict 
    self._batch_setitems(obj.iteritems()) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 663, in _batch_setitems 
    save(v) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 306, in save 
    rv = reduce(self.proto) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py", line 70, in _reduce_ex 
    raise TypeError, "can't pickle %s objects" % base.__name__ 
TypeError: can't pickle instance method objects 

Nos puede hacerlo mejor, por cierto, si se utiliza una mejor serializador. El uso de dill en lugar de pickle permite serializar la mayoría de los objetos. El escabeche del dict es mucho más complicado, como puedes ver a continuación.

>>> import dill 
>>> dill.dumps(d) 
'\x80\x02}q\x00(U\x01aq\x01K\x01U\x01cq\x02cdill.dill\n_load_type\nq\x03U\nMethodTypeq\x04\x85q\x05Rq\x06cdill.dill\n_create_function\nq\x07(cdill.dill\n_unmarshal\nq\x08T$\x01\x00\x00c\x01\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00C\x00\x00\x00s#\x00\x00\x00d\x01\x00j\x00\x00t\x01\x00d\x02\x00\x84\x00\x00|\x00\x00j\x02\x00\x83\x00\x00D\x83\x01\x00\x83\x01\x00\x83\x01\x00S(\x03\x00\x00\x00Ns\n\x00\x00\x00MyDict({})c\x01\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00s\x00\x00\x00s\x15\x00\x00\x00|\x00\x00]\x0b\x00}\x01\x00|\x01\x00V\x01q\x03\x00d\x00\x00S(\x01\x00\x00\x00N(\x00\x00\x00\x00(\x02\x00\x00\x00t\x02\x00\x00\x00.0t\x01\x00\x00\x00i(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>s\t\x00\x00\x00<genexpr>\x03\x00\x00\x00s\x02\x00\x00\x00\x06\x00(\x03\x00\x00\x00t\x06\x00\x00\x00formatt\x04\x00\x00\x00dictt\x05\x00\x00\x00items(\x01\x00\x00\x00t\x04\x00\x00\x00self(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x08\x00\x00\x00__repr__\x02\x00\x00\x00s\x02\x00\x00\x00\x00\x01q\t\x85q\nRq\x0bc__builtin__\n__main__\nU\x08__repr__q\x0cNN}q\rtq\x0eRq\x0fNcdill.dill\n_create_type\nq\x10(h\x03U\x08TypeTypeq\x11\x85q\x12Rq\x13U\x06MyDictq\x14h\x03U\x08DictTypeq\x15\x85q\x16Rq\x17\x85q\x18}q\x19(U\n__module__q\x1aU\x08__main__q\x1bh\x0ch\x0fU\x07__doc__q\x1cNutq\x1dRq\x1e\x87q\x1fRq U\x01bq!K\x02u.' 
>>> pickletools.dis(dill.dumps(d)) 
    0: \x80 PROTO  2 
    2: } EMPTY_DICT 
    3: q BINPUT  0 
    5: ( MARK 
    6: U  SHORT_BINSTRING 'a' 
    9: q  BINPUT  1 
    11: K  BININT1 1 
    13: U  SHORT_BINSTRING 'c' 
    16: q  BINPUT  2 
    18: c  GLOBAL  'dill.dill _load_type' 
    40: q  BINPUT  3 
    42: U  SHORT_BINSTRING 'MethodType' 
    54: q  BINPUT  4 
    56: \x85  TUPLE1 
    57: q  BINPUT  5 
    59: R  REDUCE 
    60: q  BINPUT  6 
    62: c  GLOBAL  'dill.dill _create_function' 
    90: q  BINPUT  7 
    92: (  MARK 
    93: c   GLOBAL  'dill.dill _unmarshal' 
    115: q   BINPUT  8 
    117: T   BINSTRING 'c\x01\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00C\x00\x00\x00s#\x00\x00\x00d\x01\x00j\x00\x00t\x01\x00d\x02\x00\x84\x00\x00|\x00\x00j\x02\x00\x83\x00\x00D\x83\x01\x00\x83\x01\x00\x83\x01\x00S(\x03\x00\x00\x00Ns\n\x00\x00\x00MyDict({})c\x01\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00s\x00\x00\x00s\x15\x00\x00\x00|\x00\x00]\x0b\x00}\x01\x00|\x01\x00V\x01q\x03\x00d\x00\x00S(\x01\x00\x00\x00N(\x00\x00\x00\x00(\x02\x00\x00\x00t\x02\x00\x00\x00.0t\x01\x00\x00\x00i(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>s\t\x00\x00\x00<genexpr>\x03\x00\x00\x00s\x02\x00\x00\x00\x06\x00(\x03\x00\x00\x00t\x06\x00\x00\x00formatt\x04\x00\x00\x00dictt\x05\x00\x00\x00items(\x01\x00\x00\x00t\x04\x00\x00\x00self(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x08\x00\x00\x00__repr__\x02\x00\x00\x00s\x02\x00\x00\x00\x00\x01' 
    414: q   BINPUT  9 
    416: \x85   TUPLE1 
    417: q   BINPUT  10 
    419: R   REDUCE 
    420: q   BINPUT  11 
    422: c   GLOBAL  '__builtin__ __main__' 
    444: U   SHORT_BINSTRING '__repr__' 
    454: q   BINPUT  12 
    456: N   NONE 
    457: N   NONE 
    458: }   EMPTY_DICT 
    459: q   BINPUT  13 
    461: t   TUPLE  (MARK at 92) 
    462: q  BINPUT  14 
    464: R  REDUCE 
    465: q  BINPUT  15 
    467: N  NONE 
    468: c  GLOBAL  'dill.dill _create_type' 
    492: q  BINPUT  16 
    494: (  MARK 
    495: h   BINGET  3 
    497: U   SHORT_BINSTRING 'TypeType' 
    507: q   BINPUT  17 
    509: \x85   TUPLE1 
    510: q   BINPUT  18 
    512: R   REDUCE 
    513: q   BINPUT  19 
    515: U   SHORT_BINSTRING 'MyDict' 
    523: q   BINPUT  20 
    525: h   BINGET  3 
    527: U   SHORT_BINSTRING 'DictType' 
    537: q   BINPUT  21 
    539: \x85   TUPLE1 
    540: q   BINPUT  22 
    542: R   REDUCE 
    543: q   BINPUT  23 
    545: \x85   TUPLE1 
    546: q   BINPUT  24 
    548: }   EMPTY_DICT 
    549: q   BINPUT  25 
    551: (   MARK 
    552: U    SHORT_BINSTRING '__module__' 
    564: q    BINPUT  26 
    566: U    SHORT_BINSTRING '__main__' 
    576: q    BINPUT  27 
    578: h    BINGET  12 
    580: h    BINGET  15 
    582: U    SHORT_BINSTRING '__doc__' 
    591: q    BINPUT  28 
    593: N    NONE 
    594: u    SETITEMS (MARK at 551) 
    595: t   TUPLE  (MARK at 494) 
    596: q  BINPUT  29 
    598: R  REDUCE 
    599: q  BINPUT  30 
    601: \x87  TUPLE3 
    602: q  BINPUT  31 
    604: R  REDUCE 
    605: q  BINPUT  32 
    607: U  SHORT_BINSTRING 'b' 
    610: q  BINPUT  33 
    612: K  BININT1 2 
    614: u  SETITEMS (MARK at 5) 
    615: . STOP 
highest protocol among opcodes = 2 

Dill serializa el método de clase porque hay funciones adicionales que se han registrado a dill que sabe cómo pepinillo y unpickle una mayor variedad de objetos - se puede ver en el código desensamblado (que comienzan con dill.dill) . Es un pepinillo mucho más grande, pero generalmente funciona para cualquier contenido que tengas en un dict.

>>> from numpy import * 
>>> everything = dill.dumps(globals()) 

Para las clases derivadas de dict, usted no tiene que preocuparse de tener objetos unpicklable dentro de los métodos de la clase - sin embargo, los contenidos de la costumbre dict siguen siendo serializado con la instancia de clase, por lo tienes que preocuparte de tener objetos no serializables dentro de tu clase.

Python 2.7.9 (default, Dec 11 2014, 01:21:43) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> 
>>> import pickle 
>>> class MyDict(dict): 
... def __repr__(self): 
...  return "MyDict({})".format(dict(i for i in self.items())) 
... 
>>> m = MyDict(a = lambda x:x) 
>>> m 
MyDict({'a': <function <lambda> at 0x10892b230>}) 
>>> pickle.dumps(a) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
NameError: name 'a' is not defined 
>>> pickle.dumps(m) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1374, in dumps 
    Pickler(file, protocol).dump(obj) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump 
    self.save(obj) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 331, in save 
    self.save_reduce(obj=obj, *rv) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 401, in save_reduce 
    save(args) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save 
    f(self, obj) # Call unbound method with explicit self 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 562, in save_tuple 
    save(element) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save 
    f(self, obj) # Call unbound method with explicit self 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict 
    self._batch_setitems(obj.iteritems()) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 663, in _batch_setitems 
    save(v) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save 
    f(self, obj) # Call unbound method with explicit self 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 748, in save_global 
    (obj, module, name)) 
pickle.PicklingError: Can't pickle <function <lambda> at 0x10892b230>: it's not found as __main__.<lambda> 

Un lambda falla para serializar porque no tiene un nombre que puede referirse a pickle. Volviendo al dill, vemos que esto funciona, sin embargo.

>>> import dill 
>>> dill.dumps(m) 
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x06MyDictq\x05h\x01U\x08DictTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x08__repr__q\x0fcdill.dill\n_create_function\nq\x10(cdill.dill\n_unmarshal\nq\x11T$\x01\x00\x00c\x01\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00C\x00\x00\x00s#\x00\x00\x00d\x01\x00j\x00\x00t\x01\x00d\x02\x00\x84\x00\x00|\x00\x00j\x02\x00\x83\x00\x00D\x83\x01\x00\x83\x01\x00\x83\x01\x00S(\x03\x00\x00\x00Ns\n\x00\x00\x00MyDict({})c\x01\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00s\x00\x00\x00s\x15\x00\x00\x00|\x00\x00]\x0b\x00}\x01\x00|\x01\x00V\x01q\x03\x00d\x00\x00S(\x01\x00\x00\x00N(\x00\x00\x00\x00(\x02\x00\x00\x00t\x02\x00\x00\x00.0t\x01\x00\x00\x00i(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>s\t\x00\x00\x00<genexpr>\x03\x00\x00\x00s\x02\x00\x00\x00\x06\x00(\x03\x00\x00\x00t\x06\x00\x00\x00formatt\x04\x00\x00\x00dictt\x05\x00\x00\x00items(\x01\x00\x00\x00t\x04\x00\x00\x00self(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x08\x00\x00\x00__repr__\x02\x00\x00\x00s\x02\x00\x00\x00\x00\x01q\x12\x85q\x13Rq\x14c__builtin__\n__main__\nh\x0fNN}q\x15tq\x16Rq\x17U\x07__doc__q\x18Nutq\x19Rq\x1a)\x81q\x1bU\x01aq\x1ch\x10(h\x11U\\c\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00C\x00\x00\x00s\x04\x00\x00\x00|\x00\x00S(\x01\x00\x00\x00N(\x00\x00\x00\x00(\x01\x00\x00\x00t\x01\x00\x00\x00x(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x08\x00\x00\x00<lambda>\x01\x00\x00\x00s\x00\x00\x00\x00q\x1d\x85q\x1eRq\x1fc__builtin__\n__main__\nU\x08<lambda>q NN}q!tq"Rq#s}q$b.' 
Cuestiones relacionadas