I extended dict
in a simple way to directly access it's values with the d.key
notation instead of d['key']
:
class ddict(dict):
def __getattr__(self, item):
return self[item]
def __setattr__(self, key, value):
self[key] = value
Now when I try to pickle it, it will call __getattr__
to find __getstate__
, which is neither present nor necessary. The same will happen upon unpickling with __setstate__
:
>>> import pickle
>>> class ddict(dict):
... def __getattr__(self, item):
... return self[item]
... def __setattr__(self, key, value):
... self[key] = value
...
>>> pickle.dumps(ddict())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __getattr__
KeyError: '__getstate__'
How do I have to modify the class ddict
in order to be properly pickable?
The problem is not
pickle
but that your__getattr__
method breaks the expected contract by raisingKeyError
exceptions. You need to fix your__getattr__
method to raiseAttributeError
exceptions instead:Now
pickle
is given the expected signal for a missing__getstate__
customisation hook.From the
object.__getattr__
documentation:(bold emphasis mine).
If you insist on keeping the
KeyError
, then at the very least you need to skip names that start and end with double underscores and raise anAttributeError
just for those:Note that you probably want to give your
ddict()
subclass an empty__slots__
tuple; you don't need the extra__dict__
attribute mapping on your instances, since you are diverting attributes to key-value pairs instead. That saves you a nice chunk of memory per instance.Demo:
That
pickle
tests for the__getstate__
method on the instance rather than on the class as is the norm for special methods, is a discussion for another day.First of all, I think you may need to distinguish between instance attribute and class attribute. In Python official document Chapter 11.1.4 about pickling, it says:
instances of such classes whose dict or the result of calling getstate() is picklable (see section The pickle protocol for details).
Therefore, the error message you're getting is when you try to pickle an instance of the class, but not the class itself - in fact, your class definition will just pickle fine.
Now for pickling an object of your class, the problem is that you need to call the parent class's serialization implementation first to properly set things up. The correct code is: