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.'