Python hash() Function

Python’s built-in hash(object) function takes one object as an argument and returns its hash value. As the hash value is calculated based on the object’s data, two different but equal objects must have the same hash value. It doesn’t follow, though, that two objects with the same hash value are equal—they can have the same hash value and still be different.

ArgumentobjectAn object for which the hash value should be calculated.
Return ValueintReturns the calculated hash value.
Python hash() Function

Learn Python hash() by Example

The basic example shows the hash value of an integer is the integer itself:

>>> hash(42)
42

However, the hash value of a string is completely unpredictable—at least for you without knowing the underlying implementation of the hash() function:

>>> hash('42')
-7286207279771019371

Hash values of floats are either the converted integer where this conversion doesn’t lose any information, or a random integer where the float has a non-zero fractional value:

>>> hash(42.0)
42
>>> hash(42.1)
230584300921372714

You can also calculate hash values of tuples and other immutable collection types. The resulting hash() value is a combination of the hash() values inside the immutable collection.

>>> hash((1, 2, 3))
2528502973977326415

However, you cannot calculate the hash() value of mutable collection types such as lists, sets, and dictionaries because this would mean that each modification of the collection would have to modify the hash value. This is impossible because hash values must be static for the same object. That’s why Python throws an error if you try to calculate the hash value of a mutable collection type.

Unhashable list:

>>> hash([1, 2, 3])
Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    hash([1, 2, 3])
TypeError: unhashable type: 'list'

Unhashable set:

>>> hash({1, 2, 3})
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    hash({1, 2, 3})
TypeError: unhashable type: 'set'

Unhashable dict:

>>> hash({'Alice': 18, 'Bob': 19})
Traceback (most recent call last):
  File "<pyshell#14>", line 1, in <module>
    hash({'Alice': 18, 'Bob': 19})
TypeError: unhashable type: 'dict'

Python hash() Video

But before we move on, I’m excited to present you my new Python book Python One-Liners (Amazon Link).

If you like one-liners, you’ll LOVE the book. It’ll teach you everything there is to know about a single line of Python code. But it’s also an introduction to computer science, data science, machine learning, and algorithms. The universe in a single line of Python!

The book was released in 2020 with the world-class programming book publisher NoStarch Press (San Francisco).

Link: https://nostarch.com/pythononeliners

Implementing Python hash() for Custom Objects

What if you define your own class—how to modify the computation of the hash() value?

For example, say you create a class Customer that has one value, the customer’s value for your company:

class Customer:
    def __init__(self, value):
        self.value = value


alice = Customer(1000)
bob = Customer(1000)

print('Alice Hash Value:', hash(alice))
print('Bob Hash Value:', hash(bob))

The output may be the following two hash values:

Alice Hash Value: -9223371944682613630
Bob Hash Value: 92172188494

The problem with this is that both should have the same hash value as they should be considered equal. So, how can you modify the output of the hash() function on a custom object?

Internally, Python calls the object.__hash__() dunder method to calculate the hash(object) value. The only thing for you to do is to overwrite its behavior. You can calculate the hash value of the custom object as a combination of the hash value of its attributes using hash() function calls on those!


class Customer:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return hash(self.value)

alice = Customer(1000)
bob = Customer(1000)
print('Alice Hash Value:', hash(alice))
print('Bob Hash Value:', hash(bob))

The hash value of a Customer object now is the hash value of the associated integer attribute value:

Alice Hash Value: 1000
Bob Hash Value: 1000

Both objects have the same hash value now!

Can Different Objects Have the Same Hash Value?

As the hash value is calculated based on the object’s data, two different but equal objects must have the same hash value. In the following example, we create two tuple objects with the same content. But they’re still different objects as proven by the call t1 is t2 which results in False.

>>> t1 = (1, 2)
>>> t2 = (1, 2)
>>> t1 is t2
False

However, the hash() value of both is the same!

>>> hash(t1)
3713081631934410656
>>> hash(t2)
3713081631934410656
>>> hash(t1) == hash(t2)
True

It doesn’t follow, though, that two objects with the same hash value are equal—they can have the same hash value and still be different.

Relationship __eq__() and __hash__()

Note that it is a good practice and avoids many subsequent problems to also implement the __eq__() method when overwriting __hash__(). Otherwise, two objects with the same hash value can still be considered different when using the == comparison. This would be illogical because the same hash value indicates that they’re considered equal!

# BAD PRACTICE 
# (no __eq__ method)
class Customer:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return hash(self.value)


alice = Customer(1000)
bob = Customer(1000)
print('Alice Hash Value:', hash(alice))
print('Bob Hash Value:', hash(bob))
print('Are they equal?', bob == alice)

The inconsistent output indicates that although Alice and Bob have the same hash value, they’re still considered unequal.

Alice Hash Value: 1000
Bob Hash Value: 1000
Are they equal? False

Instead, the correct example would be the following where the __eq__ method is overwritten.

# GOOD PRACTICE 
# (defined __eq__ method)
class Customer:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return hash(self.value)

    def __eq__(self, other):
        return self.value == other.value

alice = Customer(1000)
bob = Customer(1000)
print('Alice Hash Value:', hash(alice))
print('Bob Hash Value:', hash(bob))
print('Are they equal?', bob == alice)

Now, the output is more consistent:

Alice Hash Value: 1000
Bob Hash Value: 1000
Are they equal? True

Summary

Python’s built-in hash(object) function takes one object as an argument and returns its hash value.

>>> hash(42)
42

As the hash value is calculated based on the object’s data, two different but equal objects must have the same hash value.

>>> t1 = (1, 2)
>>> t2 = (1, 2)
>>> hash(t1)
3713081631934410656
>>> hash(t2)
3713081631934410656
>>> hash(t1) == hash(t2)
True
>>> t1 is t2
False

It doesn’t follow, though, that two objects with the same hash value are equal—they can have the same hash value and still be different.

Where to Go From Here?

Enough theory, let’s get some practice!

To become successful in coding, you need to get out there and solve real problems for real people. That’s how you can become a six-figure earner easily. And that’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?

Practice projects is how you sharpen your saw in coding!

Do you want to become a code master by focusing on practical code projects that actually earn you money and solve problems for people?

Then become a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.

Join my free webinar “How to Build Your High-Income Skill Python” and watch how I grew my coding business online and how you can, too—from the comfort of your own home.

Join the free webinar now!