Why aren't Python sets hashable?

PythonHashSet

Python Problem Overview


I stumbled across a blog post detailing how to implement a powerset function in Python. So I went about trying my own way of doing it, and discovered that Python apparently cannot have a set of sets, since set is not hashable. This is irksome, since the definition of a powerset is that it is a set of sets, and I wanted to implement it using actual set operations.

>>> set([ set() ])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

Is there a good reason Python sets are not hashable?

Python Solutions


Solution 1 - Python

Generally, only immutable objects are hashable in Python. The immutable variant of set() -- frozenset() -- is hashable.

Solution 2 - Python

Because they're mutable.

If they were hashable, a hash could silently become "invalid", and that would pretty much make hashing pointless.

Solution 3 - Python

From the Python docs:

> hashable
> An object is hashable if it > has a hash value which never changes > during its lifetime (it needs a > hash() method), and can be compared to other objects (it needs an > eq() or cmp() method). Hashable objects which compare equal > must have the same hash value. > > Hashability makes an object usable as > a dictionary key and a set member, > because these data structures use the > hash value internally. > > All of Python’s immutable built-in > objects are hashable, while no mutable > containers (such as lists or > dictionaries) are. Objects which are > instances of user-defined classes are > hashable by default; they all compare > unequal, and their hash value is their > id().

Solution 4 - Python

In case this helps... if you really need to convert unhashable things into hashable equivalents for some reason you might do something like this:

from collections import Hashable, MutableSet, MutableSequence, MutableMapping

def make_hashdict(value):
    """
    Inspired by https://stackoverflow.com/questions/1151658/python-hashable-dicts
     - with the added bonus that it inherits from the dict type of value
       so OrderedDict's maintain their order and other subclasses of dict() maintain their attributes
    """
    map_type = type(value)

    class HashableDict(map_type):
        def __init__(self, *args, **kwargs):
            super(HashableDict, self).__init__(*args, **kwargs)
        def __hash__(self):
            return hash(tuple(sorted(self.items())))

    hashDict = HashableDict(value)

    return hashDict


def make_hashable(value):
    if not isinstance(value, Hashable):
        if isinstance(value, MutableSet):
            value = frozenset(value)
        elif isinstance(value, MutableSequence):
            value = tuple(value)
        elif isinstance(value, MutableMapping):
            value = make_hashdict(value)

        return value

my_set = set()
my_set.add(make_hashable(['a', 'list']))
my_set.add(make_hashable({'a': 1, 'dict': 2}))
my_set.add(make_hashable({'a', 'new', 'set'}))

print my_set

My HashableDict implementation is the simplest and least rigorous example from here. If you need a more advanced HashableDict that supports pickling and other things, check the many other implementations. In my version above I wanted to preserve the original dict class, thus preserving the order of OrderedDicts. I also use AttrDict from here for attribute-like access.

My example above is not in any way authoritative, just my solution to a similar problem where I needed to store some things in a set and needed to "hashify" them first.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDan BurtonView Question on Stackoverflow
Solution 1 - PythonSven MarnachView Answer on Stackoverflow
Solution 2 - PythonphihagView Answer on Stackoverflow
Solution 3 - PythonJohn Gaines Jr.View Answer on Stackoverflow
Solution 4 - Pythonflutefreak7View Answer on Stackoverflow