There is an way to do this, but it will require you to make your own class, or get clever. You can register the original ids while pickling, and set an unpickling function to look up the created object if it has been unpickled, or create it if it hasn't.
I have a quick example using the __reduce__
below. But you should probably know that this isn't the best idea in the first place.
It may be easier to use the copyreg
library, but you should know that anything that you do with this library will affect anything you pickle all the time. The __reduce__
method will be cleaner and safer as you are explicitly telling pickle
which classes you expect to have this behavior, instead of applying them implicitly to everything.
There are worse caveats to this system. The id will always change between python instances, so you need to store the original id during the __init__
(or __new__
, however you do it) and make sure that now defunct value is maintained when it's pulled out of the shelve later. Uniqueness of id isn't even guaranteed within a python session due to garbage collection. I'm sure other reasons not to do this will come up. (I'll try to address them with my class, but I make no promises.)
import uuid
class UniquelyPickledDictionary(dict):
_created_instances = {}
def __init__(self, *args, _uid=None, **kwargs):
super().__init__(*args, **kwargs)
self.uid = _uid
if _uid is None:
self.uid = uuid.uuid4()
UniquelyPickledDictionary._created_instances[self.uid] = self
def __reduce__(self):
return UniquelyPickledDictionary.create, (self.uid,), None, None, list(self.items())
@staticmethod
def create(uid):
if uid in UniquelyPickledDictionary._created_instances:
return UniquelyPickledDictionary._created_instances[uid]
return UniquelyPickledDictionary(_uid=uid)
The uuid
library should be more unique than the object ids in the long run. I forget what guarantees they hold, but I believe this is not multiprocessing safe.
An equivalent version using the copyreg can be made to pickle any class, but will require special handling on unpickling to guarantee repickling points to the same object. To make it the most general, a check against the "already created" dictionary would have to be made to compare against all the instances. To make it the most usable, a new value has to be added to the instance, which may not be possible if the object uses __slots__
(or in a few other cases).
I'm using 3.6, but I think it should work for any still supported version of Python. It preserved the object in my testing, with recursion (but pickle already does that) and multiple unpicklings.