Let’s imagine you want to store list or dictionary as a key in python dict. And you are going to write the code like this:

dict_key = {'a': 3, 'b': 2}
stat = {}
stat[dict_key] = 1

It’s a bad code because your are trying to use mutable object as a key in dictionary. And Python will check it and this code will throw an error:

TypeError: unhashable type: 'dict'

Let’s consider how to use complex objects and data structures as dict keys in Python.

Let’s read the documentation and we see that Python doesn’t allow to store dict and list as dictionary or set key.

The reason is that dict and list are mutable and Python protect you from errors like this:

dict_key = {'a': 3, 'b': 2}
stat = {}
stat[dict_key] = 1

# Change the key and you'll not find it on dictionary
dict_key['c'] = 3
print(stat[dict_key])

Convert Dict to frozenset before use as dictionary key

Use frozenset for dictionary key-value list.

Let’s have a look at the example:

dict = {'a': 3, 'b': 2}
dict_key = frozenset(dict.items())
stat = {}
stat[dict_key] = 1

If you want to search you should also use frozenset

print(stat[dict_key])

Convert List to tuple before using as dictionary key

Let’s have a look how to do it:

list = [1, 2, 3]
list_key = tuple(list)
stat = {}
stat[list_key] = 1

If you want to search you should also use tuples:

print(stat[list_key])
# prints 1

Note: if you change order of elemenets in list you’ll not find it in dictionary. Use frozen set for it

For Objects you have to implement hashable methods

What happens if you to use your Class objects as key? By default Python uses method id() as a key. So every object will be treated as a new key. That’s not what you want to have.

Let’s have a look:

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

stat = {}
stat[Point(1, 2)] = 1
print(stat.get(Point(1, 2)))
# prints None

For proper work it should implement methods hash and eq

Let’s do it and see the result:

class Point:
	def __init__(self, x, y):
		self.x = x
		self.y = y
    
	def __hash__(self):
		return self.x ^ self.y
    
	def __eq__(self, other):
		return other and self.x == other.x and self.y == other.y

stat = {}
stat[Point(1, 2)] = 1
print(stat.get(Point(1, 2)))
# prints 1

Recap:

  • Use frozendict(dict.items()) if you want to store dict as a key in other dictionary or set;
  • Use tuple(list) if you want to store list as a key in dictionary;
  • Implement hash and eq if you want to use your Class objects as dictionary keys. Also don’t forget that it should be immutable.

Stay tuned, check out my blog for more useful tips and tricks.