Asking "is hashable" about a Python value

PythonHash

Python Problem Overview


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" ?

Python Solutions


Solution 1 - Python

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).

Solution 2 - Python

def hashable(v):
    """Determine whether `v` can be hashed."""
    try:
        hash(v)
    except TypeError:
        return False
    return True

Solution 3 - Python

All hashable built in python objects have a .__hash__() method. You can check for that.

olddict = {"a":1, "b":{"test":"dict"}, "c":"string", "d":["list"] }

for key in olddict:
   if(olddict[key].__hash__):
      print str(olddict[key]) + " is hashable"
   else: 
      print str(olddict[key]) + " is NOT hashable"

output

1 is hashable
string is hashable
{'test': 'dict'} is NOT hashable
['list'] is NOT hashable

Solution 4 - Python

Why not use duck typing?

for key in olddict:
   try:
       newdict[olddict[key]] = key
   except TypeError:
       newdict[str(olddict[key])] = key

Solution 5 - Python

I guess the best solution is to use collections.Hashable:

import collections
if isinstance(olddict[key], collections.Hashable):
    newdict[olddict[key]] = key
else:
    newdict[str(olddict[key])] = key

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
QuestionPaul NathanView Question on Stackoverflow
Solution 1 - PythonMark ByersView Answer on Stackoverflow
Solution 2 - PythonNed BatchelderView Answer on Stackoverflow
Solution 3 - PythonChandlerView Answer on Stackoverflow
Solution 4 - PythondansalmoView Answer on Stackoverflow
Solution 5 - PythonKamyarView Answer on Stackoverflow