I had the need to implement a hashable dict so I could use a dictionary as a key for another dictionary.
A few months ago I used this implementation: Python hashable dicts
However I got a notice from a colleague saying 'it is not really immutable, thus it is not safe. You can use it, but it does make me feel like a sad Panda'.
So I started looking around to create one that is immutable. I have no need to compare the 'key-dict' to another 'key-dict'. Its only use is as a key for another dictionary.
I have come up with the following:
class HashableDict(dict): """Hashable dict that can be used as a key in other dictionaries""" def __new__(self, *args, **kwargs): # create a new local dict, that will be used by the HashableDictBase closure class immutableDict = dict(*args, **kwargs) class HashableDictBase(object): """Hashable dict that can be used as a key in other dictionaries. This is now immutable""" def __key(self): """Return a tuple of the current keys""" return tuple((k, immutableDict[k]) for k in sorted(immutableDict)) def __hash__(self): """Return a hash of __key""" return hash(self.__key()) def __eq__(self, other): """Compare two __keys""" return self.__key() == other.__key() # pylint: disable-msg=W0212 def __repr__(self): """@see: dict.__repr__""" return immutableDict.__repr__() def __str__(self): """@see: dict.__str__""" return immutableDict.__str__() def __setattr__(self, *args): raise TypeError("can't modify immutable instance") __delattr__ = __setattr__ return HashableDictBase()
I used the following to test the functionality:
d = {"a" : 1} a = HashableDict(d) b = HashableDict({"b" : 2}) print a d["b"] = 2 print a c = HashableDict({"a" : 1}) test = {a : "value with a dict as key (key a)", b : "value with a dict as key (key b)"} print test[a] print test[b] print test[c]
which gives:
{'a': 1}
{'a': 1}
value with a dict as key (key a)
value with a dict as key (key b)
value with a dict as key (key a)
as output
Is this the 'best possible' immutable dictionary that I can use that satisfies my requirements? If not, what would be a better solution?
However, neither a list nor another dictionary can serve as a dictionary key, because lists and dictionaries are mutable.
Values can be any type of object, but keys must be immutable. This means keys could be integers, strings, or tuples, but not lists, because lists are mutable. Dictionaries themselves are mutable, so entries can be added, removed, and changed at any time.
What are Keys? As shown in the figure below, keys are immutable ( which cannot be changed ) data types that can be either strings or numbers. However, a key can not be a mutable data type, for example, a list. Keys are unique within a Dictionary and can not be duplicated inside a Dictionary.
Why must dictionary keys be immutable? ¶ The hash table implementation of dictionaries uses a hash value calculated from the key value to find the key. If the key were a mutable object, its value could change, and thus its hash could also change.
If you are only using it as a key for another dict
, you could go for frozenset(mutabledict.items())
. If you need to access the underlying mappings, you could then use that as the parameter to dict
.
mutabledict = dict(zip('abc', range(3))) immutable = frozenset(mutabledict.items()) read_frozen = dict(immutable) read_frozen['a'] # => 1
Note that you could also combine this with a class derived from dict
, and use the frozenset
as the source of the hash, while disabling __setitem__
, as suggested in another answer. (@RaymondHettinger's answer for code which does just that).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With