Pickle/dill cannot handle circular references if __hash__ is overridden

Viewed 1056

Consider the following MWE:

#import dill as pickle      # Dill exhibits similar behavior
import pickle

class B:
    def __init__(self):
        self.links = set()

class A:
    def __init__(self, base: B):
        self.base = base
        base.links.add(self)

    def __hash__(self):
        return hash(self.base)

    def __eq__(self, other):
        return self.base == other.base

pickled = pickle.dumps(A(B()))  # Success
print(pickle.loads(pickled))    # Not so much

The above example fails with the following exception:

Traceback (most recent call last):
  File "./mwe.py", line 26, in <module>
    print(pickle.loads(pickled))
  File "./mwe.py", line 18, in __hash__
    return hash(self.base)
AttributeError: 'A' object has no attribute 'base'

As I understand the problem, pickle attempts to deserialize B.links before it deserializes A. The set instance used in B attempts to invoke A.__hash__ at some point, and since the instance of A is not yet fully constructed, it cannot compute its own hash, making everyone sad.

How do I get around this without breaking circular references? (breaking the cycles would be a lot of work because the object I'm trying to serialize is hilariously complex)

1 Answers
Related