profile

Rodrigo Girão Serrão

🐍📝 5 ways to create dictionaries in Python

published3 months ago
6 min read

Hey there, how are you doing?

In this issue of the Mathspp Insider I will tell you about the 5 fundamental ways in which you can create dictionaries in Python, discussing the differences and nuances of each method.


@ Decorators workshop

On the 17th of September at 4pm UTC, I will be giving a 2h remote workshop on the topic of decorators. After attending, you will be able to answer these questions:

  • What is a decorator?
  • How can I use a decorator?
  • How to implement a decorator?
  • What useful decorators are there in the Standard Library?

The workshop is not appropriate for lazy people. I will be talking as little as possible and I will give you plenty of practice exercises for you to write as much code as possible.

The workshop is at the introductory level of decorators, so you do not need to be an expert to join. The main pre-requisite is that you are curious and willing to learn.

You can sign-up for the workshop here or by clicking the big button below. If you have any questions, you can reply to this email and ask everything! I will reply shortly :)

See you there!


Creating dictionaries, 5 ways

The dictionary is a built-in type in Python that works as a key-value mapping: you give it a key and you get a value back. Knowing how to work with dictionaries is very important if you want to be a proficient Pythonista.

You cannot work with dictionaries if you don't create them first, so I will walk you through the 5 fundamental ways in which you can create dictionaries in Python. Most people I know have never used technique #5, so make sure to take a look and let me know if you used it already.

Literal dictionary syntax

When you see curly brackets {} in Python you probably think of dictionaries, and that is because curly brackets are used in the syntax for dictionary literals. Here is an example:

me = {
    "name": "Rodrigo",
    "twitter": "mathsppblog",
    "knows_python": True,
}

In the literal notation, the curly brackets {} denote the beginning and end of the dictionary, and inside the brackets you write all your key-value pairs. The key-value pairs are separated by commas, and the keys are separated from the values with colons. So, the dictionary above has 3 key-value pairs:

  1. the key "name" maps to the value "Rodrigo";
  2. the key "twitter" maps to the value "@mathsppblog"; and
  3. the key "knows_python" maps to the value True.

After the dictionary is defined, I can access the values by passing in the keys inside square brackets, much like if you were indexing a list:

>>> me["name"]
'Rodrigo'
>>> me["twitter"]
'mathsppblog'
>>> me["knows_python"]
True

The literal syntax is great: it is very explicit and easy to read, and you can use the literal syntax to define all your dictionaries.

However, some disadvantages of literal syntax are:

  • it becomes cumbersome to write down all the key-value pairs for large dictionaries; and
  • large dictionaries take up a lot of space in your code file.

Dictionary comprehensions

To circumvent the burden of having to write down all the key-value pairs of large dictionaries, Python provides a convenient syntax for when you want to define your dictionaries programmatically. For example, look at the dictionary below:

months = {
    "Jan": "January",
    "Feb": "February",
    "Mar": "March",
    "Apr": "April",
    "May": "May",
    "Jun": "June",
    "Jul": "July",
    "Aug": "August",
    "Sep": "September",
    "Oct": "October",
    "Nov": "November",
    "Dec": "December",
}

On a scale from 1 to BORING, how tedious do you think it was to type that out?

Despite being a large dictionary (or at least, large enough to be annoying to type explicitly), the key-value pairs of that dictionary exhibit a very clear pattern: the first three letters of the month name map to the full month name. Thus, by using a dictionary comprehension, that dictionary becomes very easy to recreate:

# Typing the month names is a bit less annoying:
>>> month_names = "January February March April May June July August September October November December"

# Dictionary comprehension:
>>> months = {month[:3]: month for month in month_names.split()}

# Some values:
>>> months["Jan"]
'January'
>>> months["Sep"]
'September'

By using a dictionary comprehension, that is very similar to a list comprehension, we managed to recreate the dictionary without having to type all the month names and the three-letter abbreviations and the opening and closing quotes and the commas. We boiled it down to writing all the month names in a string. And if being careful about the capitalisation of the month names is already too much work, we could've done that with Python too:

# Month names in a string:
>>> month_names = "january february match april may june july august september october november december"

# Dictionary comprehension:
>>> months = {month[:3].capitalize(): month.capitalize() for month in month_names.split()}

# Some values:
>>> months["Jan"]
'January'
>>> months["Sep"]
'September'

Heck, you can even do without having to write the month names yourself! Just use the module calendar:

>>> import calendar
>>> months = {month[:3]: month for month in calendar.month_name[1:]}
>>> months["Jan"]
'January'
>>> months["Sep"]
'September'

Dictionary comprehensions are very useful when you know how to generate your dictionary programmatically, especially if you are creating your new dictionary out of another one or out of another iterable.

Dictionary comprehensions are covered in detail in my book “Comprehending Comprehensions”, so you can always check that out.

Built-in dict and an iterable of pairs

The built-in dict can take an iterable with key-value pairs. This is not a very common way of building dictionaries, but this is especially useful when you have all of your keys in one iterable and all of your values in another iterable. In those cases, you just have to put keys and values together with the built-in zip. Here is an example mapping integers to their names:

>>> keys = range(1, 5)
>>> values = ["one", "two", "three", "four"]
>>> dict(zip(keys, values))
{1: 'one', 2: 'two', 3: 'three', 4: 'four'}

The pairs that the built-in dict expects do not have to come from the built-in zip. For instance, in this specific example we could have used the built-in enumerate directly:

>>> dict(enumerate(values, start=1))
{1: 'one', 2: 'two', 3: 'three', 4: 'four'}

When your keys and values come from separate data sources, this is the preferred way to build your dictionary. Sadly, it is not a very common way to build a dictionary. (If you have a context in which this technique is convenient frequently, let me know!)

Built-in dict and keyword arguments

The built-in dict can also be used in a second way, if you provide it keyword arguments. All keyword arguments to the built-in dict are taken to be key-value pairs in the resulting dictionary. As a direct consequence of this definition, the built-in dict and the keyword arguments can only be used to build dictionaries where all keys are strings and valid variable names.

Here is an example of this in action:

# Values don't have to be strings:
>>> dict(one=1, two=2)
{'one': 1, 'two': 2}

# Keys have to be valid variable names:
>>> dict(name="Rodrigo", twitter="mathsppblog")
{'name': 'Rodrigo', 'twitter': 'mathsppblog'}

As I said, the keys have to be valid variable names, though. It does not suffice for the keys to be strings. For example, try creating the dictionary below with the built-in dict and keyword arguments... You will fail!

>>> {"1": "one"}
{'1': 'one'}  # the key is a string

As someone pointed out to me recently, using keyword arguments is much neater than using literal syntax because it avoids many quotes, but this is a technique that obviously only works on certain types of dictionaries: the ones where all keys are valid variable names.

By the way, if your goal is just to make a mess, you can mix the built-in dict and iterable of pairs technique with keyword arguments:

>>> dict(enumerate("zero one two".split()), three="four")
{0: 'zero', 1: 'one', 2: 'two', 'three': 'four'}

Class method dict.fromkeys

The final fundamental way in which you can create dictionaries is by using the class method dict.fromkeys. This is a very powerful technique but alas it only applies to a specific set of dictionaries: the ones where the values are all the same.

The class method accepts an iterable of keys and a default value, and builds a dictionary where all keys map to that value:

>>> dict.fromkeys(range(5), ";)")
{0: ';)', 1: ';)', 2: ';)', 3: ';)', 4: ';)'}
>>> dict.fromkeys(["likes", "retweets"], 0)
{'likes': 0, 'retweets': 0}

The value to be used can be omitted and the default value is None:

# Default value is None:
>>> dict.fromkeys("abc")
{'a': None, 'b': None, 'c': None}
>>> dict.fromkeys(range(5))
{0: None, 1: None, 2: None, 3: None, 4: None}

The class method has a gotcha associated with it, though. Be careful when using mutable values, because the value isn't copied to each key; instead, it is exactly the same object that is used over and over again:

>>> d = dict.fromkeys(range(3), [])
>>> d
{0: [], 1: [], 2: []}

>>> d[0].append("zero")
>>> d[1].append("one")
>>> d[2]  # Shouldn't d[2] be empty?!
['zero', 'one']
>>> d
{0: ['zero', 'one'], 1: ['zero', 'one'], 2: ['zero', 'one']}  # 🤯

Again, this is an issue with mutability. If you want, I can write about it later. Just let me know you are interested!

This is a very nice technique, though, and it has a couple of very neat use cases. The nicest one I know is that you can use dict.fromkeys to remove duplicates from a list (or another iterable) while preserving order:

twitter profile avatar
Rodrigo 🐍📝
Twitter Logo
@mathsppblog
August 28th 2022
27
Retweets
157
Likes

These are the 5 fundamental ways in which you can create Python dictionaries. Did you know all of them?


Thanks for reading, and I'll see you next time!

Rodrigo.

P.S. don't forget to sign-up for the workshop on decorators!