GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • ZetCode
  • All
  • Golang
  • Python
  • C#
  • Java
  • JavaScript
  • Subscribe
  • Twitter
  • Github
  • Privacy
  • About

This view

  • Original page
  • New address

GeezerVison is showing zetcode.com — Python hashing - hash function, hashable objects, __hash__, hashlib

Ebooks

  • PyQt5 ebook
  • Tkinter ebook
  • SQLite Python
  • wxPython ebook
  • Windows API ebook
  • Java Swing ebook
  • Java games ebook
  • MySQL Java ebook

Python hashing

last modified October 30, 2026 Python 3.15

In this article we explain the hashing concept in Python. We cover hash tables, the hash function, hashable objects, and cryptographic hashes with the hashlib module.

Hash table

Hash tables are used to implement map and set data structures in many programming languages, such as C++, Java, and Python. Python uses hash tables for dictionaries and sets. A hash table is a collection of key-value pairs, where each key is unique. Hash tables offer efficient lookup, insert, and delete operations; on average, they take constant time regardless of the size of the table.

Hashing

Hashing is the process of using an algorithm to map data of any size to a value of a fixed size. This value is called a hash value or a hash. Hash values are computed with hash functions. A hash table uses the hash of a key to compute the slot where the key and its value are stored, so it does not have to search the whole table.

Different keys may have the same hash value; this is called a collision. Therefore, after finding a slot by the hash, the table also compares the keys for equality.

Python hashable

An object is hashable if it has a hash value which never changes during its lifetime and if it can be compared to other objects. A hashable object needs a __hash__ method and an __eq__ method.

Note: Hashable objects which compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally. Python immutable built-in objects are hashable; mutable containers (such as lists, dictionaries, or sets) are not. Instances of user-defined classes are hashable by default. They all compare unequal (except with themselves), and their hash value is derived from their id.

Python hash function

The hash built-in function returns the hash value of an object by calling its __hash__ method. Hash values are integers.

builtin_hashables.py
print(hash(100))
print(hash(3.14))
print(hash('falcon'))
print(hash((1, 2, 3)))
print(hash(frozenset({1, 2, 3})))
print(hash(None))

The example prints the hash values of an integer, a float, a string, a tuple, a frozen set, and None. The hash of a small integer is the integer itself. A tuple and a frozen set are hashable because all their elements are hashable.

$ python builtin_hashables.py
100
322818021289917443
214523777757570393
529344067295497451
-272375401224217160
4238894112

Hash randomization

The hash values of strings and bytes are salted with a random value that is chosen when the interpreter starts. This protects web applications against denial-of-service attacks that send many keys with colliding hashes. The hash of a string is therefore constant within one run of a program, but it differs between runs.

str_hash.py
print(hash('falcon'))
print(hash(b'falcon'))
print(hash(42))

We run the program several times. The hashes of the string and the bytes object change; the hash of the integer does not. The PYTHONHASHSEED environment variable fixes the seed; the value 0 disables the randomization. We should never store hash values or rely on them being the same across runs.

$ python str_hash.py
-5709079237076841186
-5709079237076841186
42
$ python str_hash.py
-4474288123343302275
-4474288123343302275
42
$ PYTHONHASHSEED=0 python str_hash.py
6462072199549027567
6462072199549027567
42
$ PYTHONHASHSEED=0 python str_hash.py
6462072199549027567
6462072199549027567
42

Hashes of numbers

Numbers that compare equal have the same hash, even if they are of different types.

numeric_hashes.py
print(hash(1), hash(1.0), hash(True))
print(hash(-1), hash(-2))
print(hash(2**61 - 1), hash(2**61))

vals = {1: 'int', 1.0: 'float', True: 'bool'}
print(vals)

Since 1 == 1.0 == True, the three values have the same hash. In a dictionary, they are the same key; the first key is kept and the value is overwritten. The hash of -1 is -2, because -1 is reserved as an error code in the C implementation of CPython. Integer hashes are computed modulo the prime number 2**61 - 1 on 64-bit platforms.

$ python numeric_hashes.py
1 1 1
-2 -2
0 1
{1: 'bool'}

Unhashable objects

Mutable containers do not have a hash value. Calling hash on them raises a TypeError.

unhashable.py
from collections.abc import Hashable

items = [[1, 2], {'a': 1}, {1, 2}, (1, [2, 3]), (1, 2), frozenset({1, 2})]

for item in items:
    try:
        print(f'{item!r:<18} {hash(item)}')
    except TypeError as e:
        print(f'{item!r:<18} {e}')

print(isinstance([1, 2], Hashable))
print(isinstance((1, 2), Hashable))

A list, a dictionary, and a set are unhashable. A tuple is hashable only if all its elements are hashable. The Hashable abstract base class from the collections.abc module checks whether a type defines a hash; it does not look at the elements of a tuple.

$ python unhashable.py
[1, 2]             unhashable type: 'list'
{'a': 1}           unhashable type: 'dict'
{1, 2}             unhashable type: 'set'
(1, [2, 3])        unhashable type: 'list'
(1, 2)             -3550055125485641917
frozenset({1, 2})  -1826646154956904602
False
True

How a dictionary uses the hash

The following example shows when a dictionary calls the __hash__ and __eq__ methods of its keys.

dict_lookup.py
class Key:

    def __init__(self, name):
        self.name = name

    def __hash__(self):
        print(f'__hash__({self.name})')
        return len(self.name)

    def __eq__(self, other):
        print(f'__eq__({self.name}, {other.name})')
        return self.name == other.name


data = {}
data[Key('sky')] = 1
data[Key('owl')] = 2
data[Key('falcon')] = 3

print('lookup')
print(data[Key('owl')])

To provoke collisions, the hash of a key is the length of its name, so sky and owl have the same hash. When owl is inserted, the dictionary finds the slot occupied by a key with the same hash and calls __eq__ to tell the keys apart. During the lookup, the dictionary computes the hash and compares the candidate keys with __eq__ until it finds the matching one.

$ python dict_lookup.py
__hash__(sky)
__hash__(owl)
__eq__(sky, owl)
__hash__(falcon)
lookup
__hash__(owl)
__eq__(sky, owl)
__eq__(owl, owl)
2

Default hash of custom objects

Python custom objects are hashable by default. Their hash is derived from their identity.

custom_object.py
class User:

    def __init__(self, name, occupation):
        self.name = name
        self.occupation = occupation


u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

print(hash(u1))
print(hash(u2))
print(u1 == u2)
print(len({u1, u2}))

We create two instances of a User with the same data. Since the class does not define __eq__, the objects are compared by identity. They have different hashes, they are not equal, and a set keeps both of them.

$ python custom_object.py
8378367468696
8378367460477
False
2

Defining __eq__ only

In the second example, we implement a custom __eq__ method.

custom_object2.py
class User:

    def __init__(self, name, occupation):
        self.name = name
        self.occupation = occupation

    def __eq__(self, other):
        if not isinstance(other, User):
            return NotImplemented
        return (self.name, self.occupation) == (other.name, other.occupation)


u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

print(u1 == u2)
print(User.__hash__)

try:
    users = {u1, u2}
except TypeError as e:
    print(e)

Now the users compare equal. However, a class that defines __eq__ without __hash__ gets its __hash__ set to None, so its instances are unhashable and cannot be inserted into a set. Since Python 3.14, the error message also tells how the object was used. Returning NotImplemented for other types lets Python try the reflected comparison.

$ python custom_object2.py
True
None
cannot use 'User' as a set element (unhashable type: 'User')

Defining __eq__ and __hash__

In the third example, we implement both the __eq__ and the __hash__ methods.

custom_object3.py
class User:

    def __init__(self, name, occupation):
        self.name = name
        self.occupation = occupation

    def __eq__(self, other):
        if not isinstance(other, User):
            return NotImplemented
        return (self.name, self.occupation) == (other.name, other.occupation)

    def __hash__(self):
        return hash((self.name, self.occupation))

    def __repr__(self):
        return f'User({self.name!r}, {self.occupation!r})'


u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')
u3 = User('Roger Roe', 'driver')

users = {u1, u2, u3}
print(users)
print(User('Roger Roe', 'driver') in users)

The __hash__ method returns the hash of a tuple of the same attributes that __eq__ compares. This guarantees that equal objects have equal hashes. The two John Doe users are now one set member, and a new object with the same data is found in the set.

$ python custom_object3.py
{User('Roger Roe', 'driver'), User('John Doe', 'gardener')}
True

Mutating a hashed object

The hash of an object must not change while the object is stored in a set or used as a dictionary key.

mutable_key.py
class User:

    def __init__(self, name, occupation):
        self.name = name
        self.occupation = occupation

    def __eq__(self, other):
        if not isinstance(other, User):
            return NotImplemented
        return (self.name, self.occupation) == (other.name, other.occupation)

    def __hash__(self):
        return hash((self.name, self.occupation))


u1 = User('John Doe', 'gardener')
users = {u1}
print(u1 in users)

u1.occupation = 'programmer'

print(u1 in users)
print(any(u is u1 for u in users))

After we change the occupation, the hash of the user changes. The set still keeps the object in the slot computed from the old hash, so the membership test looks in the wrong slot and returns False, although the object is in the set. Objects whose __hash__ depends on mutable attributes should not be modified while they are stored in hash-based collections.

$ python mutable_key.py
True
False
True

Hashing with dataclasses

The dataclass decorator generates the __eq__ method. By default, it sets __hash__ to None, which makes the instances unhashable. If both eq and frozen are True, it generates a __hash__ method from the fields.

dataclass_hash.py
from dataclasses import FrozenInstanceError, dataclass


@dataclass
class Point:
    x: int
    y: int


@dataclass(frozen=True)
class User:
    name: str
    occupation: str


print(Point.__hash__)

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

print(u1 == u2)
print(hash(u1) == hash(u2))
print({u1, u2})

try:
    u1.occupation = 'programmer'
except FrozenInstanceError as e:
    print(e)

The plain Point dataclass is unhashable. The frozen User dataclass is hashable; equal users have equal hashes. The fields of a frozen dataclass are read-only, so the hash cannot change; an assignment raises FrozenInstanceError.

$ python dataclass_hash.py
None
True
True
{User(name='John Doe', occupation='gardener')}
cannot assign to field 'occupation'

Cryptographic hashes with hashlib

The hash function is designed for hash tables. To verify data integrity or to compute fingerprints, we use cryptographic hash functions from the hashlib module. They return the same digest on every run and on every platform.

hashlib_digest.py
import hashlib

msg = 'an old falcon'.encode()

print(hashlib.md5(msg).hexdigest())
print(hashlib.sha256(msg).hexdigest())
print(hashlib.sha3_256(msg).hexdigest())
print(hashlib.blake2b(msg, digest_size=16).hexdigest())

h = hashlib.sha256()
h.update(b'an old ')
h.update(b'falcon')
print(h.hexdigest())

Hash functions work with bytes, so the string is encoded first. The hexdigest method returns the digest as a hexadecimal string. The update method feeds data in chunks; the result is the same as hashing the whole message at once. MD5 is broken and should be used only for non-security purposes, such as checksums. For storing passwords, use a slow key derivation function such as hashlib.scrypt instead.

$ python hashlib_digest.py
adf7c99320726d960ea9e8d817695e11
f3e6f76094b8eab3ec68bbed6e608f476e338eb4bc033242188d58c9e829fc5e
c0426b8d94b30a5bc7dce521cb89507cd49b591d047ac5c82698e9e0281a0a15
d8e0f5ce933e07c324c91138e8021351
f3e6f76094b8eab3ec68bbed6e608f476e338eb4bc033242188d58c9e829fc5e

Hashing a file

Since Python 3.11, the hashlib.file_digest function computes the digest of a file object opened in binary mode. It reads the file in chunks, so large files do not have to fit into memory.

file_digest.py
import hashlib

with open('words.txt', 'w') as f:
    f.write('sky\nblue\nfalcon\nowl\n')

with open('words.txt', 'rb') as f:
    digest = hashlib.file_digest(f, 'sha256')

print(digest.name)
print(digest.hexdigest())

We compute the SHA-256 digest of a small text file. The sha256sum tool gives the same result.

$ python file_digest.py
sha256
c6deeb637a2d398a917805621867634a5169c0516eae153586e171ea780393cc
$ sha256sum words.txt
c6deeb637a2d398a917805621867634a5169c0516eae153586e171ea780393cc  words.txt

Source

Python hash function - Python documentation

In this article we have covered hashing in Python.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.