__hash__
__hash__(self) int · Added in vPython 2.6 · Updated March 19, 2026 · Dunder Methods Overview
The __hash__ method defines the hash value for objects of your class. Hash values are integers used by Python’s dictionary and set implementations to quickly locate objects during lookups. When you use an object as a dictionary key or add it to a set, Python calls __hash__ internally.
Signature
def __hash__(self) -> int:
# Return an integer hash value for the object
Default Behavior
By default, Python uses the object’s identity (its memory address) as the hash value. This means two distinct objects that are not the same object in memory will have different hashes by default:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(1, 2)
print(hash(p1) == hash(p2)) # False — different objects, different hashes
# output: False
print(p1 == p2) # False — different objects are not equal
# output: False
This default hashability applies to all user-defined classes that do not define __eq__.
The __eq__ and __hash__ Contract
This is the most important gotcha in Python’s data model. When you define __eq__ without defining __hash__, Python sets __hash__ to None, making your objects unhashable:
class Person:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return isinstance(other, Person) and self.name == other.name
# This raises TypeError — Person is now unhashable
people = {Person("Alice")} # TypeError: unhashable type 'Person'
Python makes this implicit because of the hash-equality contract: if two objects are equal (a == b returns True), they must have the same hash value. If you define custom equality but Python kept the default identity-based hash, you could have two objects that compare equal but have different hashes — which would break dict and set lookups.
To make your class hashable again, explicitly define __hash__:
class Person:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return isinstance(other, Person) and self.name == other.name
def __hash__(self):
return hash(self.name)
alice1 = Person("Alice")
alice2 = Person("Alice")
print(alice1 == alice2) # True — equal objects
# output: True
print(hash(alice1) == hash(alice2)) # True — same hash required by contract
# output: True
people = {alice1} # Works — Person is now hashable
print(alice2 in people) # True
# output: True
Hashable Classes in the Standard Library
Many built-in types are hashable:
# Immutable types are hashable
print(hash(42)) # 42
# output: 42
print(hash("hello")) # Integer
# output: 1093245322886368395
print(hash((1, 2, 3))) # Tuple of hashables is hashable
# output: 529344067295497451
# Mutable types are NOT hashable
try:
hash([1, 2, 3])
except TypeError as e:
print(e) # unhashable type: 'list'
# output: unhashable type: 'list'
Implementing __hash__
Based on a single attribute
The simplest hash implementation hashes the same attributes used in __eq__:
class User:
def __init__(self, user_id: int):
self.user_id = user_id
def __eq__(self, other):
return isinstance(other, User) and self.user_id == other.user_id
def __hash__(self):
return hash(self.user_id)
Based on multiple attributes
When __eq__ compares multiple attributes, hash all of them:
class Coordinate:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def __eq__(self, other):
return isinstance(other, Coordinate) and self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
Returning a constant
Valid but restricts all instances to the same hash bucket (slows down dict/set operations):
class UniqueId:
def __init__(self, id_value):
self.id_value = id_value
def __eq__(self, other):
return isinstance(other, UniqueId) and self.id_value == other.id_value
def __hash__(self):
return 0 # Works but all instances hash to the same bucket
Making Objects Unhashable
To intentionally make a class non-hashable (even if it would otherwise be hashable), explicitly set __hash__ = None:
class Cache:
def __init__(self):
self.data = {}
def __eq__(self, other):
return isinstance(other, Cache) and self.data == other.data
__hash__ = None # Explicitly unhashable — prevents use as dict keys or in sets
This is useful for mutable objects that happen to implement __eq__ for comparison but should never be used in sets or as dictionary keys.
Gotchas
1. Hash collisions are allowed, but equality collisions break lookups
Different objects can have the same hash (a collision). Python’s dict/set handles collisions internally. What breaks lookups is when objects are equal but have different hashes:
# This is fine — same hash, equal objects
class HashStomper:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return isinstance(other, HashStomper) and self.value == other.value
def __hash__(self):
return 0 # All instances have the same hash — works but slow
2. Hash must be consistent across an object’s lifetime
The hash value should not change while the object is in a dict or set. If you mutate an attribute that contributes to the hash, lookups will fail:
class Mutable:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return isinstance(other, Mutable) and self.name == other.name
def __hash__(self):
return hash(self.name)
m = Mutable("key")
s = {m} # Add to set
m.name = "changed" # Mutate the hash-contributing attribute
print(m in s) # Now unpredictable — hash changed while in set
3. If __eq__ returns NotImplemented, __hash__ is used directly
When a comparison method returns NotImplemented, Python does not fall back to __hash__ for that side. The hash is computed normally for dict/set operations:
# In dict lookup, if key.__eq__(other) returns NotImplemented,
# Python tries other.__eq__(key). If that also returns NotImplemented,
# objects are compared by identity (id()).
Summary
__hash__returns an integer used for dict and set lookups- Default hash for user-defined classes uses object identity (
id()) - Defining
__eq__without__hash__makes a class unhashable (Python sets__hash__ = None) - If you define
__eq__, you must define__hash__to return the same hash for equal objects - Hash values must not change while an object is in a dict or set
- To intentionally make a class unhashable, set
__hash__ = None