Sobes.tech
Junior

What are hashable data types?

sobes.tech AI

Answer from AI

Hashable data types in Python are immutable types, whose instances have a fixed hash value throughout their lifetime. This value is computed using a hash function and is used for quick lookup of elements in sets and keys in dictionaries.

Properties of hashable types:

  • Immutability: The object cannot be changed after creation.
  • Presence of __hash__ method: This method should return an integer hash value of the object. If the method is not defined or returns None, the object is considered unhashable.
  • Presence of __eq__ method: This method should define object equality. If two objects are equal (a == b), then their hash values should be equal (hash(a) == hash(b)). The reverse is not necessarily true: two objects can have the same hash value but not be equal (hash collision).

Examples of built-in hashable types:

  • Numbers (int, float, complex):
    # Examples
    print(hash(123))
    print(hash(3.14))
    print(hash(1 + 2j))
    
  • Strings (str):
    # Example
    print(hash("hello"))
    
  • Tuples (tuple): Provided all elements of the tuple are hashable.
    # Example
    print(hash((1, 2, "a")))
    
  • Boolean values (bool):
    # Example
    print(hash(True))
    
  • NoneType:
    # Example
    print(hash(None))
    

Examples of unhashable built-in types:

  • Lists (list): Mutable.
    # Example
    # list = [1, 2]
    # print(hash(list)) # Will raise TypeError
    
  • Sets (set): Mutable.
    # Example
    # my_set = {1, 2}
    # print(hash(my_set)) # Will raise TypeError
    
  • Dictionaries (dict): Mutable.
    # Example
    # my_dict = {"a": 1}
    # print(hash(my_dict)) # Will raise TypeError
    

Usage of hashable types:

  • Dictionary keys must be hashable.
    # Example
    my_dict = {"apple": 1, (1, 2): 2} # Allowed
    # my_dict = {[1, 2]: 3} # Not allowed, will raise TypeError
    
  • Set elements must be hashable.
    # Example
    my_set = {"a", 1, (2, 3)} # Allowed
    # my_set = {"a", [1, 2]} # Not allowed, will raise TypeError
    

Custom classes are hashable by default if they inherit from a class that defines __hash__, or if they do not define __eq__ and __hash__. If only __eq__ is defined, the class becomes unhashable. You can make a custom class hashable by defining __eq__ and __hash__ methods that meet your requirements. For immutable objects, you can use the @total_ordering decorator along with defining __eq__ and other comparison methods.

# Example of a custom immutable and hashable class
from functools import total_ordering

@total_ordering
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return self.x == other.x and self.y == other.y

    def __hash__(self):
        # Hash of a tuple containing hashes of the elements
        return hash((self.x, self.y))

# Usage in a dictionary
p1 = Point(1, 2)
 p2 = Point(1, 2)
my_dict = {p1: "coordinates"}
print(p2 in my_dict) # True, because objects are equal and hashable