Search code examples
pythonhash

Asking "is hashable" about a Python value


I am interested in taking an arbitrary dict and copying it into a new dict, mutating it along the way.

One mutation I would like to do is swap keys and value. Unfortunately, some values are dicts in their own right. However, this generates a "unhashable type: 'dict'" error. I don't really mind just stringifying the value and giving it the key. But, I'd like to be able to do something like this:

for key in olddict:
  if hashable(olddict[key]):
    newdict[olddict[key]] = key
  else
    newdict[str(olddict[key])] = key

Is there a clean way to do this that doesn't involve trapping an exception and parsing the message string for "unhashable type" ?


Solution

  • Python 3.x

    Use collections.abc.Hashable or typing.Hashable.

    >>> import typing
    >>> isinstance({}, typing.Hashable)
    False
    >>> isinstance(0, typing.Hashable)
    True
    

    Note: both are the same one, the latter is simply an alias of the former. Also note that collections.Hashable was removed in Python 3.10+ (deprecated since 3.7).

    Python 2.6+ (an original answer)

    Since Python 2.6 you can use the abstract base class collections.Hashable:

    >>> import collections
    >>> isinstance({}, collections.Hashable)
    False
    >>> isinstance(0, collections.Hashable)
    True
    

    This approach is also mentioned briefly in the documentation for __hash__.

    Doing so means that not only will instances of the class raise an appropriate TypeError when a program attempts to retrieve their hash value, but they will also be correctly identified as unhashable when checking isinstance(obj, collections.Hashable) (unlike classes which define their own __hash__() to explicitly raise TypeError).