Creating Hashable and Unhashable Objects in Python
__hash__.Default Hashability
All Python objects are instances of some class. Even classes themselves are instances of the class type. Class instances in Python are hashable by default. They inherit it from object, the base class of all classes in Python 3.
Both objects, the class itself and the class instance, are hashable.
class A: pass print(A.__class__) # <class 'type'> print(A.mro()) # [<class '__main__.A'>, <class 'object'>] a = A() print(hash(A)) # 97365552112 print(hash(a)) # 97365511141
Built-in functions and types are hashable (except for mutable containers such as lists, dicts, sets). A tuple is hashable if all of its elements are hashable.
# <class 'builtin_function_or_method'> print(hash(sorted)) # 8631570007220 # <class 'Exception'> print(hash(Exception("Error"))) # 124008387560 # <class 'str'> print(hash("Python")) # 6279743517120683530 # <class 'int'> print(hash(123456)) # 123456 t = (1, 2, 3) print(hash(t)) # 529344067295497451 t = (1, 2, []) print(hash(t)) # TypeError: unhashable type: 'list'
Generators, iterators, file objects, etc.
# <class 'generator'> g = (x for x in "abc") print(hash(g)) # 138921020124 # <class 'list_iterator'> i = iter([1, 2, 3, 4, 5]) print(hash(i)) # 138921358108 # <class '_io.TextIOWrapper'> f = open("test.py") print(hash(f)) # 138921319366
Modules, user-defined functions are also hashable.
import random # random <class 'module'> print(hash(random)) # 164856203943 def foo(): pass # foo <class 'function'> print(hash(foo)) # 164856560844
Overriding the __hash__
Classes can define their hash function by overriding the __hash__ method. If the hash value is calculated from self._value, that value must not be a mutable object.
class A: def __init__(self, value): self._value = value def __hash__(self): return hash(self._value) # or return my_hash_algorithm(self._value)
It is recommended that user classes define both __hash__() and __eq__() methods.
# hash(a) == hash(a1) and a == a1 class A: def __init__(self, value: str): self._value = value def __hash__(self): return hash(self._value) def __eq__(self, other): return self._value == other._value a = A("qwerty") a1 = A("qwerty") print(hash(a)) # -5570161944640390331 print(hash(a1)) # -5570161944640390331 print(a == a1) # True print(a is a1) # False
These objects are treated as equals in sets or as dictionary keys.
s = {a, a1}
print(s)
# {<__main__.A object at 0x000001B22F1DBE50>}
print(a in s, a1 in s)
# True True
for i in s:
print(i is a)
# True
print(i is a1)
# False
d = {a: "a", a1: "a1"}
print(d)
# {<__main__.A object at 0x000001B22F1DBE50>: 'a1'}
print(d[a])
# a1
print(d[a1])
# a1
print(a in d.keys())
# True
print(a1 in d.keys())
# True
for k in d.keys():
print(k is a)
# True
print(k is a1)
# False
Instances of different classes can also be made equal.
The __hash__ and __eq__ of decimal.Decimal and fractions.Fraction are overridden. This was made to match the numerically equal integer or float.
from decimal import Decimal from fractions import Fraction d = Decimal(3.14) f = Fraction(3.14) print(hash(d)) # 322818021289917443 print(hash(f)) # 322818021289917443 print(hash(3.14)) # 322818021289917443 print(d == f, f == 3.14, d == 3.14) # True True True
We can keep the implementation of __hash__() from the parent class. If we define only the __eq__ method, then objects of this class will be unhashable.
# hash(a) != hash(a1) and a == a1 class A: # <ParentClass>.__hash__ __hash__ = object.__hash__ def __init__(self, value: str): self._value = value def __eq__(self, other): return self._value == other._value a = A("qwerty") a1 = A("qwerty") print(hash(a)) # 132341991081 print(hash(a1)) # 132341640165 print(a == a1) # True print(a is a1) # False
If the hashes are different, both objects are in a set or dict.
s = {a, a1}
print(s)
# {<__main__.A object at 0x0000028BBFC0E190>,
# <__main__.A object at 0x0000028BC0196350>}
print(a in s, a1 in s)
# True True
d = {a: "a", a1: "a1"}
print(d)
# {<__main__.A object at 0x0000028BBFC0E190>: 'a',
# <__main__.A object at 0x0000028BC0196350>: 'a1'}
print(d[a])
# a
print(d[a1])
# a1
Unhashable Objects
If we want to suppress hash support, we can include __hash__ = None in the class definition.
The class A itself still has a hash value because the class is an instance of a built-in class. We cannot modify a built-in class. The __hash__ in class A definition only applies to instances of class A.
class A: __hash__ = None a = A() print(hash(A)) # 71517693211 print(hash(a)) # TypeError: unhashable type: 'A'
The same effect can be achieved if we inherit our class from some unhashable Python type.
class A(list): pass a = A() print(hash(A)) # 119112257331 print(hash(a)) # TypeError: unhashable type: 'A'
References: