What can Python Dictionaries store?
Dictionaries contain key-value pairs (like a Map in Java), as you may know already. Besides that, dictionaries are really special compared to similar data structures in other programming languages. They benefit from the fact that Python has dynamic typing, so you can use keys and values of different types in the same dictionary without any problems. You can use any object as value and every hashable object as key, which includes functions, classes, and even datatypes! You can find out whether an object is hashable just by passing it into the built-in hash() function.
As you can see, dictionaries are really powerful and enable you to write creative and smart code with them. However, there are some limitations to the key values. For instance, mutable objects like lists are not hashable, so you can’t use them as a key.
Looping over Dictionaries
In Python, it is widespread to loop over lists, tuples, or other simple iterables, but what about dictionaries? In fact, you can loop over dictionaries the same way you loop over lists. But you may notice that you only get the keys when you try it out.
Normally you also want the values when iterating over a dictionary. The easiest approach to do this would be the following:
It works, even though it’s not really beautiful and pythonic. Luckily, there is a better solution that makes the code a bit prettier. Every dictionary has the items() method, which returns an iterator that provides a tuple containing key and value pairs.
In my opinion, the unpacking operator is probably the smartest trick when it comes to writing cool and simple code with dictionaries in Python. In short, the unpacking operator ** unpacks dictionaries to keyword arguments. Another unpacking operator * which does nearly the same thing for other iterables like tuples or lists.
You also may have seen the double stars in some function parameters, where they do exactly the opposite: they wrap all (not defined) keyword arguments into a dictionary!
However, let’s see what you can do with the unpacking operator:
Instead of writing all the keyword arguments directly in the function call, we can simply store them in a dictionary and unpack them in the function call. This makes the code more flexible and readable. Furthermore, we could go one step further and wrap this code in another function that can take any arguments and pass them to the post function! This technique can be particularly useful in larger projects where you want to generalize most of your functionality and write a lot of wrapper-functions.
Besides that, there are many other cases to use the unpacking operator. Try it out and share your use cases in the comments!
You may have heard of list comprehensions before, but this trick works for dictionaries too!
The syntax is almost the same as for list comprehensions, the only difference is that you need two values on the left side: the key and the value, separated by a colon. The middle part represents the current value of the iterable which is on the right, exactly like list comprehensions or a classic for loop.
This short and simple example shows a dictionary that stores the numbers from 0 to 99 as keys and the corresponding squared numbers as values. Sure, you could create much more complex dictionaries with this approach. However, always remember that others are reading this code too. To make your code more readable, it is often best to write a classic for loop. For this reason, you should only use dictionary comprehension in simple cases where it is clear what is happening.
Sorting a Dictionary
In general, the dictionary is not meant to be a sorted data structure. However, since Python 3.6 dictionaries keep the insertion order by default. Although dictionaries have an order based on the insertions, Python has no built-in way to sort them (like lists, for instance).
Nevertheless, we can sort a dictionary by using the feature that you can create dictionaries with a list of tuples that contain key and value pairs. So we can get those key-value tuples with the item() method, sort them and create a new dict with the sorted tuples.
The sorted() function takes the key-value tuples and sorts them. To sort them correctly, it is important to set the sorting key. This is just a function that takes the key-value tuples and returns the value by which the pairs should get sorted. In this case, we want to sort by the HTTP code (the key), which is always stored on the 0th index in each tuple. Therefore the lambda expression we use for the sorting key returns this value. If you want to sort the dictionary by the values, just return the value at index 1.
Whenever you want to get values from a dictionary it is possible that the given key doesn’t exist. In this case, Python raises a KeyError that you have to catch or the program exits. The problem with the approach to cover unknown keys is the amount of boilerplate code it produces. It makes the code less readable and more clunky.
Another way to avoid KeyErrors, is to check if the key exists before accessing it:
But this approach doesn’t really solve the problem and produces some boilerplate code, although it’s better than the previous one. Luckily, there are two ways to get a default value when a key doesn’t exist!
The easiest way that works for any dictionary is to use the get() method when retrieving data from it. The first argument for this method is the key you are looking for. The interesting part is the second argument, where you can optionally pass a default value, which will be returned when the key was not found.
Another way to get a default value when the key doesn’t exist is to use the collections module’s defaultdict data structure. The defaultdict works like a normal dictionary with the additional feature to define a default value, which always gets returned when retrieving data from an unknown key. The first argument in the constructor is a callable whose return value is used as the default argument. Every argument after that will work with the normal dictionary constructor.
This code snippet shows the same example as the previous one, but this is much smarter, right? You don’t need the get() method that is not really common, and there is always a default value, no matter how you access the dict. So everybody who uses the dictionary doesn’t need to care about setting defaults or catching a KeyError. The user can use it as a normal dictionary!
In addition, you can even use the default value when adding new entries to the dictionary. If the default value is a list, for instance, you can directly append items to the new entry:
Creating a Dictionary from two Lists
If you ever encounter the situation where two lists should create a dictionary and the items of one list represent the keys whereas the other list represents the values, there is a simple solution!
We use the feature to create a dictionary by passing a list of tuples to the constructor, where every tuple is a key-value pair. The zip function creates such a list of tuples with two elements. The alternative approach would be a for loop with a counter variable, which adds every item to the dict, but the zip-approach is clearly more beautiful. In general, you can do really cool things with the zip function combined with dictionaries. Make sure to keep this option in mind before writing ugly code!
Adding items to a dictionary is quite simple, but what about deleting items? Fair enough, deleting an item from a dictionary is really simple. However, there are a few things that you should have in mind when you want to delete items from a Python dictionary.
First of all, there are two methods how you can delete items from a dictionary:
- use the del keyword
- use the pop() method
The main difference between those possibilities is that the pop method returns the removed value, whereas del just deletes the item.
It only becomes interesting when you want to delete items while iterating over the same dict. Python will throw a RuntimeError when you try to do this because you change the dictionary’s size. Unfortunately, I can’t present you with a really cool solution for this case, but here are two different workarounds. If you know a better solution that smartly solves the problem, please let me know in the comments!
I personally don’t favor any of these approaches; I think it depends on the context which one fits better. Either way, neither of the two variants is beautiful.
If you want to merge dictionaries, you have 3 options:
- Use the update() method
Every dictionary has this method, which can take a dictionary as argument. The items of the passed dictionary get updated if they already exist or added if not. Additionally you can create a new dict by copying one of the dictionaries you want to merge before doing the update. This has the advantage that you don’t change the original dicts when you merge them.
- Merge with the unpacking operator
A much simpler approach to merge dicts is to simply create a new dictionary with the items from both dicts. You can use the previously explained unpacking operator for this.
- Use the merge operator (since Python 3.9)
Python 3.9 introduced the single pipe | as the merge operator for dictionaries. If you run your project with Python version 3.9 or newer, this might be the best approach.
Making copies of a Dictionary
With the final dictionary trick, I want to raise awareness of the things to keep in mind when copying dictionaries. Dictionaries are mutable data structures, which means that every time you pass a dictionary as an argument to a function or assign it to a new variable, the dictionary gets passed by reference. So everything you do with the dictionary in this function will affect the original one too! In comparison, when you do the same thing with a string (which is immutable), the function’s operations do not affect the original string because it gets passed by value.
This makes it harder to create copies of dictionaries. Therefore every dictionary has a copy() method, which creates a shallow copy of the dictionary. You can check this with the id() function that returns the internal id of an object.
Another important fact to keep in mind is that the copy() function only creates a shallow copy of the dictionary. This means that the items are inserted as references into the copied dictionary. If you have immutable objects as values, they will be the same in the original and the copy! To avoid this, you can use the copy module’s deepcopy() function, which will insert copies of the dictionary items.
This post presented you with 10 dictionary-tricks in Python. I hope you enjoyed them and learned new approaches to apply to your projects! There are many more cool things and tricks you can do with dictionaries. If you have one that can’t be missing here, leave a comment below!
There is nothing that excites me more than solving problems and understanding complex concepts. I want to share this enthusiasm by making complex things as simple as possible, so everyone can understand them. Nothing is more frustrating than trying to understand a topic with incomprehensible explanations. That’s why I want to inspire you to become a better developer by simplifying concepts and explaining issues clearly.