Python: Interesting facts about Dictionaries

Lesser-known usage of dictionaries in Python

Pravash
3 min readDec 5, 2023

Hi Everyone, In this article I will explore the few interesting things about Python Dictionaries.

Dictionaries in Python are versatile data structures that offer efficient key-value mapping. While their common use involves simple lookups and storage of data, there are some interesting and lesser-known aspects and use cases worth exploring:

1. Adding a List As a Dictionary’s Key

_dict = {}
_list = [1, 2, 3]
_dict[_list] = 'Added'


Output -
_dict[_list] = 'Added'
TypeError: unhashable type: 'list'

The fact is that the above code raises an error if we add a list as a dictionary’s key. The reason is whenever we add an object as a dictionary’s key, Python invokes the __𝐡𝐚𝐬𝐡__ function of that object’s class.

Unlike int, str, tuple, etc the implementation of the __𝐡𝐚𝐬𝐡__ method is missing from the list class.

So, now if we try to extend the list class and add this method inside it, It helps us to add a list as a dictionary’s key.

class ClassList(list):
def __hash__(self):
return 0

_dict = {}
_list = ClassList([1, 2, 3])
_dict[_list] = 'Added'
print(_dict)


Output -
{[1, 2, 3]: 'Added'}

The above workaround is not recommended, as this will makes the list hashable, which lead to unexpected behavior in code.

2. Using Dictionary’s as an Alternative to IF Conditions

We all know that the use of dictionary is to maintain key-value pair. But there’s another special use case of dictionaries — using them as IF conditions.

For instance, consider the code below. Here, corresponding to an input value, we invoke a specific function.

num = 1

if num == 1:
funcA()
elif num == 5:
funcB()
else:
func()

Using dictionary -

num = 1

func_mapping = {1: funcA,
5: funcB}
func_mapping.get(num, func)() # func is default function

So with a dictionary, we can directly retrieve the corresponding function by providing it with the key.

3. Dictionaries as switch statement

You can simulate a switch statement using dictionaries for cleaner and more readable code. example —

def switch_case(case):
return {
'case1': 'This is case 1',
'case2': 'This is case 2',
'default': 'This is the default case'
}.get(case, 'Invalid case')

result = switch_case('case1')

This will print — This is case 1

4. __missing__ items

From 2.5 onwards dicts have a special method __missing__ that is invoked for missing items:

class MyDict(dict):
def __missing__(self, key):
self[key] = rv = []
return rv

m = MyDict()
m["foo"].append(1)
m["foo"].append(2)

print(dict(m)) # {'foo': [1, 2]}
print(m["x"]) # []

There is also a dict subclass in collections called Defaultdict that does pretty much the same but calls a function without arguments for not existing items:

from collections import defaultdict

m = defaultdict(list)
m["foo"].append(1)
m["foo"].append(2)

print(dict(m)) # {'foo': [1, 2]}

This can be useful when you want to provide a default value or perform a specific action when a key is not found, instead of raising a KeyError.

5. hash equivalence key

Here’s an interesting example of dictionary —

my_dict = {'1': 'string', True: 'bool', 1: 'int', 1.0: float}
print(my_dict)

# o/p: {'1': 'string', True: <class 'float'>}

Despite adding 4 distinct keys to a Python dictionary, can you tell why it only preserves two of them, This is because — In Python, dictionaries find a key based on the equivalence of hash (computed using hash()), but not identity (computed using id()).

In this case, there’s no doubt that 1.0, 1, and True inherently have different datatypes and are also different objects.

print(id(1), id(True), id(1.0))
print(type(1), type(True), type(1.0))

# o/p:
# 140407572928816 4308871808 140407573652336
# <class 'int'> <class 'bool'> <class 'float'>

But, the fact is they share the same hash value, the dictionary considers them as the same keys.

print(hash(1), hash(True), hash(1.0))

# o/p: 1 1 1

And also did you see the value corresponding to True is bool, but it prints float.

o/p: {'1': 'string', True: <class 'float'>}

This is because, at first, True is added as a key and its value is 'bool'. Next, while adding the key 1, python recognizes it as an equivalence of the hash value.

Thus, the value corresponding to True is overwritten by 'int', while the key (True) is kept as is.

Finally, while adding 1.0, another hash equivalence is encountered with an existing key of True. Yet again, the value corresponding to True, which was updated to 'int' in the previous step, is overwritten by 'float'.

Conclusion

Dictionaries in Python are powerful and flexible, and exploring their various features and use cases can lead to more efficient and readable code.

Happy coding !!

--

--

Pravash

I am a passionate Data Engineer and Technology Enthusiast. Here I am using this platform to share my knowledge and experience on tech stacks.