A frozendict is a dictionary that cannot be modified after being created - but it is hashable and may serve as a member of a set or a key in a dictionary.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
* Quick'n'dirty version: class hashabledict(dict): def __hash__(self): return hash(tuple(sorted(self.items()))) * Fancy version: class frozendict(dict): def _blocked_attribute(obj): raise AttributeError, "A frozendict cannot be modified." _blocked_attribute = property(_blocked_attribute) __delitem__ = __setitem__ = clear = _blocked_attribute pop = popitem = setdefault = update = _blocked_attribute def __new__(cls, *args): new = dict.__new__(cls) dict.__init__(new, *args) return new def __init__(self, *args): pass def __hash__(self): try: return self._cached_hash except AttributeError: h = self._cached_hash = hash(tuple(sorted(self.items()))) return h def __repr__(self): return "frozendict(%s)" % dict.__repr__(self)
I came up with the quick'n'dirty version to eliminate duplicates in a list of dicts by converting it to a set. It doesn't actually verify that the dict is not modified and should be used with care.
The fancy version blocks access to methods that might change the dictionary and also caches the computed hash for better performance. The name frozendict is by analogy to the builtin frozenset type.
This implementation assumes that all dictionary keys and values are hashable and that the keys are fully comparable. It won't work with complex keys. Sorting is required because dictionary items() are returned in arbitrary order. An alternative implementation may combine item hashes using an order-independent function like xor or addition.