Reputation: 19329
In the example below pickle
crashes with every attempt to save an instance A to file.
Why does it happen?
How to avoid it?
How to get around it?
class Base(dict):
def __init__(self):
super(Base, self).__init__()
class ClassA(Base):
def __init__(self):
super(ClassA, self).__init__()
def setInstB(self, instB):
self['instB']=instB
class ClassB(Base):
def __init__(self):
super(ClassB, self).__init__()
def setInstA(self, instA):
self['instA']=instA
instA=ClassA()
instB=ClassB()
instA.setInstB(instB)
instB.setInstA(instA)
import pickle
pickle.dump( instA, open( "save.p", "wb" ) )
If Base(dict)
class is not declared to be a subclass of built-in dict
the problem goes away. Running the code posted below raises no pickle
errors. But I still want to know why pickle fails and how to make it work with Classes inheriting from dict
.
class Base(object):
def __init__(self):
super(Base, self).__init__()
class ClassA(Base):
def __init__(self):
super(ClassA, self).__init__()
def setInstB(self, instB):
self.instB=instB
class ClassB(Base):
def __init__(self):
super(ClassB, self).__init__()
def setInstA(self, instA):
self.instA=instA
instA=ClassA()
instB=ClassB()
instA.setInstB(instB)
instB.setInstA(instA)
import pickle
pickle.dump( instA, open( "save.p", "wb" ) )
Upvotes: 2
Views: 258
Reputation: 35217
It might not be a bug in pickle
, as I can pickle
what you wanted to do initially without problems. If you use dill
instead of pickle
, then there's no issue… and all dill
is is a bunch of copy_reg
functions to register how to serialize different types that pickle
can't handle by default.
dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39)
[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.
>>> class Base(dict):
... def __init__(self):
... super(Base, self).__init__()
...
>>> class ClassA(Base):
... def __init__(self):
... super(ClassA, self).__init__()
... def setInstB(self, instB):
... self['instB']=instB
...
>>> class ClassB(Base):
... def __init__(self):
... super(ClassB, self).__init__()
... def setInstA(self, instA):
... self['instA']=instA
...
>>> instA=ClassA()
>>> instB=ClassB()
>>>
>>> instA.setInstB(instB)
>>> instB.setInstA(instA)
>>>
>>> import dill as pickle
>>> pickle.dump( instA, open('save.p', 'wb') )
>>> res = pickle.load( open('save.p', 'rb') )
>>> res
{'instB': {'instA': {...}}}
>>> res['instB']
{'instA': {'instB': {...}}}
Get dill
here:
https://github.com/uqfoundation
Upvotes: 1
Reputation: 23955
I think that you ran into some kind of bug in pickle. I suggest you submit it, and probably it will get fixed.
Following code can fix your problems:
# -*- coding: utf-8 -*-
class Base(dict):
def __init__(self):
super(Base, self).__init__()
pk = None
class ClassA(Base):
def __init__(self):
super(ClassA, self).__init__()
def setInstB(self, instB):
self['instB']=instB
class ClassB(Base):
def __init__(self):
super(ClassB, self).__init__()
def setInstA(self, instA):
self['instA']=instA
instA=ClassA()
instA.pk=1
instB=ClassB()
instB.pk=2
instA.setInstB(instB)
instB.setInstA(instA)
import pickle
class MyPickler(pickle.Pickler):
def persistent_id(self, obj):
return obj.pk
MyPickler(open( "save.p", "wb" )).dump(instA)
Pickler allows explicitly to generate unqie ids for your objects: see persistent_id
method, and since these are database objects I guess that generating ids will be easy.
Upvotes: 2