Asking "is hashable" about a Python value
Solution 1
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 checkingisinstance(obj, collections.Hashable)
(unlike classes which define their own__hash__()
to explicitly raiseTypeError
).
Solution 2
def hashable(v):
"""Determine whether `v` can be hashed."""
try:
hash(v)
except TypeError:
return False
return True
Solution 3
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
Why not use duck typing?
for key in olddict:
try:
newdict[olddict[key]] = key
except TypeError:
newdict[str(olddict[key])] = key
![Paul Nathan](https://i.stack.imgur.com/UNGHL.png?s=256&g=1)
Paul Nathan
Software engineer/craftman/programmer Passion for quality and correctness as exemplified in the continuous improvement/kaizen model. Obsessive about tools and infrastructure Focused on working in Scala/Rust/Lisp/Haskell/OCaml Articulate Lisp - a Common Lisp environment tutorial site. Learn Common Lisp today!
Updated on July 09, 2022Comments
-
Paul Nathan almost 2 years
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" ?
-
Paul Nathan almost 14 yearsNed - that's exactly what I would prefer to avoid. Also, this function will trap TypeErrors that aren't "unhashable type".
-
yantrab almost 14 yearsThere's only one TypeError that hash() will raise. Or alternately, whatever TypeError hash raises, it's going to keep your value from being hashed. I'd even argue that this should catch Exception, because it doesn't really matter why hash() failed: a failure in hash() makes your value unhashable. Can you say more about why you want to avoid exceptions like this? This encapsulates the exception handling in a nice function, and makes your sample code above work perfectly.
-
Paul Nathan almost 14 yearsPerhaps it's more of a philosophical point - the question isn't about the "exceptional condition" of being unhashable due to some sort of "freak condition", but a query about the functionality available on a type. If that makes any sense.
-
yantrab almost 14 yearsI think you're right about it being a philosophical point, and perhaps comes down to a static vs. dynamic view of the world. The classic Python mindset often tries things and catches exceptions, rather than attempting to determine upfront if something in theory is possible.
-
user1066101 almost 14 years@Paul Nathan: This is the standard approach. Simply attempt the operation; if it fails, then the object was not hashable; do something else. If it works, then the object was hashable and you did what you expected to do.
-
Mark Byers almost 14 yearsA warning about this: In Python 2.5 this will give:
{'test': 'dict'}is hashable
. It can also give a wrong result in newer versions if a class defines__hash__
to raise a TypeError. -
Joshua Chia about 10 yearsNo, and this is something you can determine only at run-time since before that the content of a list is generally unknown.
hash(([],))
givesTypeError: unhashable type: 'list'
-
RedX over 7 yearsSmall warning for people using Python 2.7
isinstance(bytearray([0xa]), collections.Hashable))
returnsTrue
buthash(bytearray([0xa]))
fails withTypeError: unhashable type: 'bytearray'
. -
codykochmann almost 7 yearsAnyone have any explanations for this one?
isinstance(Decimal('sNaN'),Hashable)
returnsTrue
yet when its given to a dictionary, I getCannot hash a signaling NaN value
-
martineau over 5 yearsIn Python 3 it's
isinstance(obj, collections.abc.Hashable)
. -
David Lord over 4 yearsIn Python 3.7.5, importing directly from
collections
raisesDeprecationWarning
. Instead,from collections.abc import Hashable
. -
Kamyar about 4 yearsThis method is not good practice! Using exception for checking such things is discouraged because of stack walking and so
-
Cecil Curry about 4 yearsThis is awful. The
hash()
builtin implicitly calls the user-definedv.__hash__()
dunder method, which could conceivably raise any possible exception – not simplyTypeError
exceptions. To generalize this, replace the overly specific subclassTypeError
above with the catch-all exception superclassException
. -
Cecil Curry about 4 yearsThis is awful.
isinstance(obj, collections.abc.Hashable)
returns false positives for user-defined classes that raise exceptions from overridden__hash__()
dunder methods.collections.abc.Hashable
cannot be trusted for real-world hashable detection. Instead, the only sane solution is to attempt tohash(obj)
and reportFalse
if doing so raises any exceptions whatsoever. -
Cecil Curry about 4 yearsThis is awful, too. Simply because a type defines a
__hash__()
method does not mean that all possible instances of that type are hashable. Considerdef __hash__(self): raise ValueError('uhoh')
, for example. Instead, the only sane solution is to attempt tohash(olddict[key])
and reportFalse
if doing so raises any exceptions whatsoever. -
Rylan Schaeffer about 2 yearsCan the author please update this answer?