Unhashable Type Python

Unhashable Type Python Error Explained: How To Fix It

Have you ever seen the message “TypeError: unhashable type” when running your Python program? Do you know what to do to fix it?

The message “TypeError: unhashable type” appears in a Python program when you try to use a data type that is not hashable in a place in your code that requires hashable data. For example, as an item of a set or as a key of a dictionary.

This error can occur in multiple scenarios and in this tutorial we will analyse few of them to make sure you know what to do when you see this error.

Let’s fix it now!

Unhashable Type ‘Dict’ Python Error

To understand when this error occurs let’s replicate it in the Python shell.

We will start from a dictionary that contains one key:

>>> country = {"name": "UK"}
>>> country
{'name': 'UK'} 

Now add a second item to the dictionary:

>>> country["capital"] = "London"
>>> country
{'name': 'UK', 'capital': 'London'} 

All good so far, but here is what happens if by mistake we use another dictionary as key:

>>> info = {"language": "english"}
>>> country[info] = info["language"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict' 

The error unhashable type: ‘dict’ occurs because we are trying to use a dictionary as key of a dictionary item. By definition a dictionary key needs to be hashable.

What does it mean?

When we add a new key / value pair to a dictionary, the Python interpreter generates a hash of the key. To give you an idea of how a hash looks like let’s have a look at what the hash() function returns.

>>> hash("language")
-79422224077228785
>>> hash({"language": "english"})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict' 

You can see that we got back a hash for a string but when we tried to pass a dictionary to the hash function we got back the same “unhashable type” error we have seen before.

The unhashable type: ‘dict’ error is caused by the fact that mutable objects like dictionaries are not hashable.

Unhashable Type ‘numpy.ndarray’ Python Error

Let’s have a look at a similar error but this time for a numpy.ndarray (N-dimensional array).

>>> import numpy as np
>>> x = np.array([[1, 2, 3], [4, 5, 6]])
>>> type(x)
<class 'numpy.ndarray'> 

After defining an array using NumPy, let’s find out what happens if we try to convert the array into a set.

>>> set(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'numpy.ndarray' 

We see the “unhashable type” error again, I want to confirm if once again we see the same behaviour when we try to apply the hash() function to our ndarray.

>>> hash(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'numpy.ndarray' 

The error is exactly the same, but why are we seeing this error when converting the array into a set?

Let’s try something else…

The array we have defined before was bi-dimensional, now we will do the same test with a uni-dimensional array.

>>> y = np.array([1, 2, 3]) 
>>> y
array([1, 2, 3])
>>> type(y)
<class 'numpy.ndarray'>
>>> set(y)
{1, 2, 3} 

It worked this time.

The reason why the first conversion to a set has failed is that we were trying to create a set of NumPy arrays but a NumPy array is mutable and hence it cannot be used as element of a set.

>>> my_set = {np.array([1, 2, 3]), np.array([4, 5, 6])}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'numpy.ndarray' 

The items in a set have to be hashable. Only immutable types are hashable while mutable types like NumPy arrays are not hashable because they could change and break the lookup based on the hashing algorithm.

For example, strings are immutable so an array of strings should get converted to a set without any errors:

>>> z = np.array(['one', 'two', 'three'])
>>> type(z)
<class 'numpy.ndarray'>
>>> set(z)
{'one', 'two', 'three'} 

All good. The same behaviour we have seen also applies to normal Python lists instead of NumPy arrays.

Unhashable Type ‘Slice’ Error in Python

The error unhashable type: ‘slice’ occurs if you try to use the slice operator with a data type that doesn’t support it.

For example, you can use the slice operator to get a slice of a Python list.

But what happens if you apply the slice operator to a dictionary?

Let’s find out…

>>> user = {"name": "John", "age": 25, "gender": "male"}
>>> user[1:3]
Traceback (most recent call last):
  File "", line 1, in 
    user[1:3]
TypeError: unhashable type: 'slice'         

The slice works on indexes and that’s why it works on lists and it doesn’t work on dictionaries.

Dictionaries are made of key-value pairs and this allows to access any value by simply using the associated dictionary key.

>>> user["name"]
'John'
>>> user["age"]
25

Unhashable Type ‘List’ in Python

Here is when you can get the unhashable type ‘list’ error in Python…

Let’s create a set of numbers:

>>> numbers = {1, 2, 3, 4}
>>> type(numbers)
<class 'set'> 

All good so far, but what happens if one of the elements in the set is a list?

>>> numbers = {1, 2, 3, 4, [5, 6]}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list' 

We get back the unhashable type error, and that’s because…

The items of a Python set have to be immutable but a list is mutable. This is required because the items of a set need to be hashable and a mutable data type is not hashable considering that its value can change at any time.

The tuple is similar to a list but is immutable, let’s see if we can create a set a provide a tuple instead of a list as one of its items:

>>> numbers = {1, 2, 3, 4, (5, 6)}
>>> numbers
{1, 2, 3, 4, (5, 6)} 

No error this time.

The difference between a list and a tuple in Python is that a list is mutable, is enclosed in square brackets [ ] and is not hashable. A tuple is immutable, is enclosed in parentheses () and is hashable.

Unhashable Type ‘Set’ Python Error

It’s time to find out how you can also encounter the unhashable type error when trying to use a set as item of another set.

First of all let’s define a list of sets:

>>> numbers = [{1, 2}, {3, 4}]
>>> numbers
[{1, 2}, {3, 4}]
>>> type(numbers[0])
<class 'set'> 

It works fine because the elements of a list can be mutable.

Now, instead of defining a list of sets we will try to define a set of sets.

Start by creating an empty set:

>>> numbers = set()
>>> type(numbers)
<class 'set'> 

Then we will use the set add method to add a first item of type set to it.

>>> item = {1,2}
>>> type(item)
<class 'set'>
>>> numbers.add(item)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set' 

We get back the error unhashable type: ‘set’ because, as explained before, the items of a set have to be immutable and hashable (e.g. strings, integers, tuples).

As a workaround we can use a different datatype provided by Python: the frozenset.

The frozenset is an immutable version of the Python set data type.

Let’s convert the item set to a frozenset:

>>> item
{1, 2}
>>> type(item)
<class 'set'>
>>> frozen_item = frozenset(item)
>>> type(frozen_item)
<class 'frozenset'> 

And now add the frozenset to the empty set we have defined before:

>>> numbers
set()
>>> numbers.add(frozen_item)
>>> numbers
{frozenset({1, 2})} 

It worked!

Hash Function For Different Data Types

We have seen that the unhashable type error occurs when we use a data type that doesn’t support hashing inside a data structure that requires hashing (e.g. inside a set or as a dictionary key).

Let’s go through several Python data types to verify which ones are hashable (they provide a __hash__ method).

Mutable data types are not hashable: list, set, dictionary.

>>> my_list = []
>>> print(my_list.__hash__)
None

>>> my_set = set()
>>> print(my_set.__hash__)
None

>>> my_dict = {}
>>> print(my_dict.__hash__)
None 

As you can see above, all three data types don’t provide the __hash__ method (None returned).

Immutable data types are hashable: string, integer, float, tuple, frozenset.

>>> my_string = ''
>>> print(my_string.__hash__)
<method-wrapper '__hash__' of str object at 0x7ffc1805a2f0>

>>> my_integer = 1
>>> print(my_integer.__hash__)
<method-wrapper '__hash__' of int object at 0x103255960>

>>> my_float = 3.4
>>> print(my_float.__hash__)
<method-wrapper '__hash__' of float object at 0x7ffc0823b610>

>>> my_tuple = (1, 2)
>>> print(my_tuple.__hash__)
<method-wrapper '__hash__' of tuple object at 0x7ffc08344940>

>>> my_frozenset = frozenset({1, 2})
>>> print(my_frozenset.__hash__)
<method-wrapper '__hash__' of frozenset object at 0x7ffc0837a9e0> 

All these data types have an implementation of the __hash__ function, they are hashable.

Conclusion

We have seen several circumstances in which the unhashable type error can occur in your Python code.

Specific Python data types require hashable data, for example the items of a set have to be hashable or the keys of a Python dictionary have to be hashable.

If unhashable data is used where hashable data is required the unhashable type error is raised by the Python interpreter.

You now know how to find out the cause of the error and how to solve it potentially by replacing a Python data type that is unhashable with a data type that is hashable.

Share knowledge with your friends!

Leave a Reply

Your email address will not be published. Required fields are marked *