How to Solve "TypeError: unhashable type: 'dict'" in Python
The TypeError: unhashable type: 'dict'
error in Python occurs when you try to use a mutable dictionary (dict
) where a hashable object is required. This typically happens when you attempt to use a dictionary as a key in another dictionary or as an element of a set.
This guide explains why this error occurs and provides the correct solutions: using frozenset
for immutable sets and tuple
for ordered, immutable sequences.
Understanding Hashability and Mutability
- Hashable: An object is hashable if it has a hash value (an integer) that never changes during its lifetime. Hashable objects can be used as dictionary keys and set elements because these data structures use the hash value internally for efficient lookups.
- Immutable: An object is immutable if its value can not be changed after it's created. Examples include strings, numbers, tuples, and frozensets.
- Mutable: An object is mutable if its value can be changed after creation. Examples include lists, dictionaries, and sets.
Key Rule: In Python, most immutable objects are hashable, and most mutable objects are not hashable.
Why Dictionaries are Unhashable
Standard Python dictionaries (dict
) are mutable. You can add, remove, and modify key-value pairs after the dictionary is created. Because dictionaries are mutable, they are not hashable. Therefore, you can not use a dictionary as a key in another dictionary or as an element of a set:
my_set = {'apple', 'banana'}
# ⛔️ TypeError: unhashable type: 'set'
# another_set = {'Alice', 'Bob', my_set} # Can't put a set inside a set
# ⛔️ TypeError: unhashable type: 'set'
# my_dict = {my_set: 'fruits'} # Can't use a set as a dictionary key
Solution 1: Using frozenset
(for Unordered Collections)
If you need a set (an unordered collection of unique items) and you want to use it as a dictionary key or within another set, use frozenset
. frozenset
is an immutable version of a set:
my_set = frozenset({'apple', 'banana'}) # Create a frozenset
another_set = {'Alice', 'Bob', my_set} # OK - frozenset is hashable
print(another_set) # Output: {'Bob', frozenset({'banana', 'apple'}), 'Alice'}
print(frozenset({'apple', 'banana'}) in another_set) # Output: True
my_dict = {my_set: 'fruits'} # OK - frozenset is hashable
print(my_dict) # Output: {frozenset({'apple', 'banana'}): 'fruits'}
print(my_dict[frozenset({'apple', 'banana'})]) # Output: fruits
- Create a
frozenset
object by callingfrozenset()
with an iterable. - Because
frozenset
is immutable, it's hashable, and you can use it in these contexts.
Solution 2: Using Tuples (If Order Matters)
If the order of the elements is important, and you don't need set-like behavior (uniqueness, set operations), use a tuple
instead of a set:
my_tuple = tuple({'apple', 'banana'}) # Order may not be preserved in this case.
# or, for maintaining the intended order, convert the key-value pairs:
my_tuple = tuple({'apple':1, 'banana':2}.items())
print(my_tuple) # Output: (('apple', 1), ('banana', 2))
my_set = {'Alice', 'Bob', my_tuple}
print(my_set)
#Output: {('apple', 1), 'Alice', ('banana', 2), 'Bob'} # Order not preserved
print(tuple({'apple', 'banana'}) in my_set) # Output: False
# Order of elements matters in a tuple.
my_dict = {my_tuple: 'fruits'} # OK - frozenset is hashable
print(my_dict) # Output: {(('apple', 1), ('banana', 2)): 'fruits'}
- Tuples are immutable and ordered, so they are hashable.
- Important:
('apple', 'banana')
is different from('banana', 'apple')
when used as a dictionary key or set element.
Converting Dictionaries to Hashable Representations
If your dictionary contains values of type dict, you can convert them to a hashable representation before using them as dictionary keys, or elements of a set:
Converting to a Tuple of Items
Use the items()
method and cast to a tuple:
dict_key = {'id': 1, 'country': 'Austria'}
my_tuple = tuple(dict_key.items()) # Convert to tuple of (key, value) pairs
print(my_tuple) # Output: (('id', 1), ('country', 'Austria'))
my_dict = {'name': 'Bob', my_tuple: 'address'}
print(my_dict) # Output: {'name': 'Bob', (('id', 1), ('country', 'Austria')): 'address'}
Converting Keys to a Tuple
dict_key = {'id': 1, 'country': 'Austria'}
my_tuple = tuple(dict_key) # Convert to tuple of keys
print(my_tuple) # Output: ('id', 'country')
- Converting a dictionary to a tuple will only create a tuple of the dictionary's keys.
Converting to a JSON String (with Caveats)
You can use json.dumps
to get the string representation of a dictionary, because strings are hashable:
import json
my_json = json.dumps({'country': 'Austria'})
my_dict = {'name': 'Bob', my_json: 'address'}
print(my_dict[json.dumps({'country': 'Austria'})]) #Output: address
- Make sure that you are using the
json.dumps
both when declaring the key, and when accessing it.
Checking if an object is hashable
You can check if an object is hashable by passing it to the built-in hash()
function:
print(hash('tutorialreference.com')) # Output: -17541182257683754
# ⛔️ TypeError: unhashable type: 'dict'
# print(hash({'name': 'Tom Nolan'}))
- The
hash()
method returns the hash value of the object, if it is hashable, otherwise it raises an exception.
Conclusion
The TypeError: unhashable type: 'dict'
error arises because dictionaries are mutable.
- To use dictionary-like data as keys or set elements, you must use an immutable representation:
frozenset
(if you need set-like behavior and order doesn't matter) ortuple
(if order is important). - Using
json.dumps
provides another approach, by serializing the dictionary to a JSON string. - Understanding mutability and hashability is essential for avoiding this error and writing correct Python code.