__hash__

__hash__(self)
Returns: int · Added in vPython 2.6 · Updated March 19, 2026 · Dunder Methods
hash dunder-methods hashable sets dictionaries

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

See Also

  • hash() — Returns the hash value of an object; calls __hash__ internally
  • dunder-eq — The __eq__ method; defining it without __hash__ makes objects unhashable
  • set — Unordered collection requiring hashable elements