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().
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__(). |
a == b), their hash values MUST be identical (hash(a) == hash(b)).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
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'
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
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
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 |
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
hash() for cryptographic verification or password storage. Built-in hashes are salted per process and subject to collisions. Always use standard library hashlib.sha256 for cryptography.
# 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...
Deepen your hands-on mastery of object hashing, custom hashable classes, and stream deduplication in our 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 ColabRun 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!
Lists are mutable. If their contents change after insertion, their hash would change, corrupting the dictionary's internal hash bucket lookup.
Because in Python, 1 == 1.0 == True. Python's hash contract requires that any two objects that compare equal must yield identical hash values.
Python randomizes the hash seed for strings upon startup (SipHash) to protect web servers and applications against Hash-DoS attacks.
Yes. If a tuple contains any mutable element (such as a list: (1, [2, 3])), calling hash() on it raises a TypeError.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.