Python hash(): Generating Hash Values & Mastering Hashable Objects

Hashable and Immutable

The hash() built-in function returns the integer hash value of an immutable object, serving as the foundation for fast dictionary lookups and set membership checks.

Hashability & Identity: In Python, only hashable objects can be used as dictionary keys or stored in sets. While id() returns an object's memory address, hash() computes an integer based on the object's value. For immutable sets, see frozenset(). To convert objects to strings, see str() and repr().

1. Syntax & Formal Parameter Specifications 🔝

The hash() function accepts a single object and returns an integer:

# Formal syntax of built-in hash()
hash_integer = hash(object)
Parameter Type Default Description & Operational Rule
object Any (hashable) Required The target object whose hash code is to be calculated.
Return int N/A A fixed-size integer representing the object's hash value.
Errors TypeError N/A Raised if the object is mutable or does not implement __hash__().

The Hashability Contract in Python

  • Constant Lifetime: An object's hash value must remain strictly constant throughout its entire lifetime.
  • Equality Invariant: If two objects compare equal (a == b), their hash values MUST be identical (hash(a) == hash(b)).
  • Dictionary & Set Foundation: Hash values allow dictionaries and sets to achieve O(1) average lookup and insertion time.

2. Hashing Immutable Built-in Data Types 🔝

Numbers, strings, tuples, and frozensets are natively hashable:

# 1. Hashing immutable built-in data types
num_int = 42
num_float = 42.0
text = "plus2net"
sample_tuple = (1, 2, 3)

print("hash(42):             ", hash(num_int))
print("hash(42.0):           ", hash(num_float))
print("Numeric equal match:  ", hash(num_int) == hash(num_float))
print("hash('plus2net'):     ", type(hash(text)).__name__)
print("hash((1, 2, 3)):      ", type(hash(sample_tuple)).__name__)

Terminal Output:

hash(42):              42
hash(42.0):            42
Numeric equal match:   True
hash('plus2net'):      int
hash((1, 2, 3)):       int

3. Unhashable Mutable Types & TypeError Traps 🔝

Mutable objects cannot be hashed because their contents can change after insertion, corrupting hash tables:

# 2. Unhashable mutable types: lists, sets, and dicts
try:
    my_list = [1, 2, 3]
    hash(my_list)
except TypeError as err:
    print("List hash error: ", err)

try:
    nested_tuple = (1, [2, 3])  # Tuple containing a mutable list
    hash(nested_tuple)
except TypeError as err:
    print("Tuple hash error:", err)

Terminal Output:

List hash error:  unhashable type: 'list'
Tuple hash error: unhashable type: 'list'

4. Implementing Custom __hash__() & __eq__() on Classes 🔝

To use custom class instances as dictionary keys or in sets, implement both __eq__() and __hash__():

# 3. Custom hashable class: implementing __hash__ and __eq__
class EmployeeRecord:
    def __init__(self, emp_id: int, name: str):
        self.emp_id = emp_id
        self.name = name

    def __eq__(self, other):
        if isinstance(other, EmployeeRecord):
            return self.emp_id == other.emp_id and self.name == other.name
        return False

    def __hash__(self):
        # Hash based on immutable tuple of identifying fields
        return hash((self.emp_id, self.name))

e1 = EmployeeRecord(101, "Alice")
e2 = EmployeeRecord(101, "Alice")
e3 = EmployeeRecord(102, "Bob")

# Because e1 == e2, they produce identical hashes and deduplicate in sets
employee_set = {e1, e2, e3}
print("Total unique employees in set:", len(employee_set))
print("Hashes match for equal objs: ", hash(e1) == hash(e2))

Terminal Output:

Total unique employees in set: 2
Hashes match for equal objs:  True

5. Python Hash Randomization (Hash-DoS Defense) 🔝

For security, Python randomizes the hash seeds of strings and bytes per interpreter session to prevent Hash-DoS attacks:

# 4. Hash randomization security mechanism (SipHash)
import sys

# Strings and bytes are randomized per interpreter process
str_val = "secure_token"
print("Hash value:            ", hash(str_val))
print("Hash randomization on: ", sys.flags.hash_randomization == 1)

Terminal Output:

Hash value:             -4778224534751702784
Hash randomization on:  True

6. Comparative Analysis: hash() vs. id() 🔝

Contrasting object value hashing with memory address identity:

Attribute hash() id()
Represents Content-based integer for hash tables Memory address of the object in CPython
Equality Rule Equal values have equal hashes (a == b → hash(a) == hash(b)) Distinct objects have distinct IDs even if values match
Applicability Only immutable / hashable objects All Python objects without exception
Session Stability Strings randomized per run Valid only for object's lifetime in memory

7. Production Recipe: High-Speed Deduplication Filter 🔝

Tracking tuple hashes allows memory-efficient stream deduplication:

# 5. Production Recipe: High-Speed Deduplication Filter
class RecordDeduplicator:
    """Deduplicates stream records by hashing immutable signature keys."""
    def __init__(self):
        self._seen_hashes = set()

    def is_duplicate(self, record: dict) -> bool:
        # Create an immutable signature tuple from dictionary keys/values
        sig = (record.get("id"), record.get("email"), record.get("status"))
        sig_hash = hash(sig)
        
        if sig_hash in self._seen_hashes:
            return True
        self._seen_hashes.add(sig_hash)
        return False

dedup = RecordDeduplicator()
r1 = {"id": 1, "email": "user@example.com", "status": "active"}
r2 = {"id": 2, "email": "test@example.com", "status": "pending"}
r3 = {"id": 1, "email": "user@example.com", "status": "active"}

print("Record 1 duplicate?", dedup.is_duplicate(r1))
print("Record 2 duplicate?", dedup.is_duplicate(r2))
print("Record 3 duplicate?", dedup.is_duplicate(r3))

Terminal Output:

Record 1 duplicate? False
Record 2 duplicate? False
Record 3 duplicate? True

8. Common Developer Pitfalls & Traps 🔝

# 6. Common traps: Cryptographic hashing vs. Python built-in hash()
# WARNING: Never use built-in hash() for passwords or security signatures!
# Built-in hash() is process-salted and non-cryptographic.
# For security, always use hashlib:
import hashlib

data = "password123".encode("utf-8")
crypto_hash = hashlib.sha256(data).hexdigest()
print("Cryptographic SHA-256 (64 hex):", crypto_hash[:24] + "...")

Terminal Output:

Cryptographic SHA-256 (64 hex): ef92b778bafe771e89245b89...

9. Interactive Notebook & Practice Exercises 🔝

Deepen your hands-on mastery of object hashing, custom hashable classes, and stream deduplication in our interactive companion Jupyter Notebook:

Interactive Companion Jupyter Notebook

Explore interactive code cells, benchmark set lookup speeds, build custom hashable entities, and run self-validating assertions.

View on GitHub Run in Google Colab

Self-Verifying Assertion Suite

Run these unit checks directly in Python or your local environment to verify hash() mechanics:

# 7. Self-verifying unit test suite for hash()
# Test 1: Integer identity hashing
assert hash(100) == 100

# Test 2: Numeric cross-type equality
assert hash(1) == hash(1.0) == hash(True) == 1
assert hash(0) == hash(0.0) == hash(False) == 0

# Test 3: Tuple hashing
t = (10, 20, "text")
assert isinstance(hash(t), int)

# Test 4: Unhashable list raises TypeError
try:
    hash([1, 2])
    assert False, "Should raise TypeError"
except TypeError:
    pass

# Test 5: Custom class equality & hash contract
class Item:
    def __init__(self, val): self.val = val
    def __eq__(self, o): return isinstance(o, Item) and self.val == o.val
    def __hash__(self): return hash(self.val)

assert hash(Item(99)) == hash(Item(99))
assert len({Item(5), Item(5)}) == 1

print("All hash() unit assertions successfully verified!")

Terminal Output:

All hash() unit assertions successfully verified!

10. Frequently Asked Questions (FAQ) 🔝

Why can't lists be used as dictionary keys?

Lists are mutable. If their contents change after insertion, their hash would change, corrupting the dictionary's internal hash bucket lookup.

Why is hash(1) equal to hash(1.0) and hash(True)?

Because in Python, 1 == 1.0 == True. Python's hash contract requires that any two objects that compare equal must yield identical hash values.

Why does hash('hello') change when I restart Python?

Python randomizes the hash seed for strings upon startup (SipHash) to protect web servers and applications against Hash-DoS attacks.

Can a tuple ever be unhashable?

Yes. If a tuple contains any mutable element (such as a list: (1, [2, 3])), calling hash() on it raises a TypeError.

Further Reading & Related Tutorials 🔝




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer