Here are 10 Python code snippets demonstrating how to implement custom hashing for classes to make them usable in sets and dictionaries:
1. Basic Custom Hashing
Implementing __hash__ and __eq__ for a simple class.
Copy
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return (self.name, self.age) == (other.name, other.age)
def __hash__(self):
return hash((self.name, self.age))
# Using in set
person1 = Person("Alice", 30)
person2 = Person("Alice", 30)
people = {person1, person2}
print(people) # Only one entry, because Person objects are hashable and equal
2. Hashing with Multiple Attributes
Create a custom class with multiple attributes, where __hash__ combines them.
Copy
3. Custom Hash for Mutable Attributes
Make sure to handle mutable attributes carefully, as they can break hash consistency.
Copy
4. Handling Hash Collisions
The __hash__ method must return a unique value for different objects, but sometimes collisions happen.
Copy
5. Using Only Immutable Attributes for Hashing
Using immutable attributes such as tuples for a hash function to guarantee consistency.
Copy
6. Custom Hash with Complex Objects
Custom classes that use complex objects, like dictionaries, for hashing.
Copy
7. Handling Non-Hashable Attributes
If an attribute is not hashable, it's crucial to exclude it from the __hash__ method.
Copy
8. Custom Hashing with String Representation
Using string representations of objects to generate unique hashes.
Copy
9. Custom Hashing with Frozen Set
Using a frozenset for hashing sets or dictionaries with unordered elements.
Copy
10. Debugging Hash and Equality
Make sure to implement __eq__ and __hash__ properly to avoid inconsistencies.
Copy
These examples demonstrate how to implement custom hashing by defining __hash__ and __eq__ methods. For custom objects to work correctly in sets and dictionaries, the hash function must be consistent with equality, and the object attributes used in the hash must be immutable and hashable.
class Product:
def __init__(self, id, name):
self.id = id
self.name = name
def __eq__(self, other):
return (self.id, self.name) == (other.id, other.name)
def __hash__(self):
return hash((self.id, self.name))
# Using in a dictionary
product1 = Product(101, "Laptop")
product2 = Product(102, "Phone")
products = {product1: "Electronics", product2: "Electronics"}
print(products)
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __eq__(self, other):
return (self.title, self.author) == (other.title, other.author)
def __hash__(self):
return hash((self.title, self.author))
# Using in set
book1 = Book("Python 101", "John Doe")
book2 = Book("Python 101", "John Doe")
books = {book1, book2}
print(books) # Should only store one book as both are equal
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
# Handling potential collisions
point1 = Point(1, 2)
point2 = Point(2, 3)
points = {point1, point2}
print(points) # Should differentiate points even if they have similar hash values
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def __eq__(self, other):
return (self.make, self.model, self.year) == (other.make, other.model, other.year)
def __hash__(self):
return hash((self.make, self.model, self.year))
car1 = Car("Toyota", "Camry", 2021)
car2 = Car("Toyota", "Camry", 2021)
cars = {car1, car2}
print(cars) # Only one entry as they are equal and hashable
class User:
def __init__(self, username, email):
self.username = username
self.email = email
def __eq__(self, other):
return self.username == other.username and self.email == other.email
def __hash__(self):
return hash((self.username, self.email))
user1 = User("john_doe", "john@example.com")
user2 = User("john_doe", "john@example.com")
users = {user1: "Admin", user2: "Admin"}
print(users) # Should show only one entry
class Employee:
def __init__(self, emp_id, name, dept):
self.emp_id = emp_id
self.name = name
self.dept = dept # This is a list, which is mutable and non-hashable
def __eq__(self, other):
return (self.emp_id, self.name) == (other.emp_id, other.name)
def __hash__(self):
return hash((self.emp_id, self.name))
employee1 = Employee(101, "Alice", ["HR"])
employee2 = Employee(102, "Bob", ["Finance"])
employees = {employee1, employee2}
print(employees) # This works even with non-hashable 'dept' attribute
class Movie:
def __init__(self, title, release_year):
self.title = title
self.release_year = release_year
def __eq__(self, other):
return self.title == other.title and self.release_year == other.release_year
def __hash__(self):
return hash(f"{self.title} {self.release_year}")
movie1 = Movie("Inception", 2010)
movie2 = Movie("Inception", 2010)
movies = {movie1, movie2}
print(movies) # Only one entry, as they are equal and hashable
class Group:
def __init__(self, members):
self.members = frozenset(members)
def __eq__(self, other):
return self.members == other.members
def __hash__(self):
return hash(self.members)
group1 = Group({"Alice", "Bob", "Charlie"})
group2 = Group({"Bob", "Alice", "Charlie"})
groups = {group1, group2}
print(groups) # Only one entry because frozensets are hashable and unordered
class Song:
def __init__(self, title, artist):
self.title = title
self.artist = artist
def __eq__(self, other):
return self.title == other.title and self.artist == other.artist
def __hash__(self):
return hash((self.title, self.artist))
song1 = Song("Shape of You", "Ed Sheeran")
song2 = Song("Shape of You", "Ed Sheeran")
song3 = Song("Blinding Lights", "The Weeknd")
songs = {song1, song2, song3}
print(songs) # Should only have 2 distinct songs (song1 and song2 are equal)