Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

D is for dictionaries - Python A to Z

Code in this blog post was written with versions: Python: 3.14

Python A-Z is a blog series about Python. Each day, I share insights, ideas and examples for different parts of Python development that match with the letter of the day. Blaugust is an annual blogging festival in August where the goal is to write a blog post every day of the month.

Dictionary is a data structure in Python that in other contexts and languages is called an associative array, akey-value store or a map. It’s a handy and efficient data structure for when you need to store and retrieve a value based on a key.

This is a 101 level introduction to dictionaries.

Dictionary

Creating a dictionary

The basic form of dictionary can be created in couple of ways:

# dict constructor with keyword arguments
scores = dict(Charlie=10, Patty=25, Snoopy=30)

# dict constructor with tuples
scores = dict(('Charlie', 10), ('Patty', 25), ('Snoopy', 30))

# key: value pairs
scores = { 'Charlie': 10, 'Patty': 25, 'Snoopy': 30 }

# dict comprehension
data = [('Charlie', 10), ('Patty', 25), ('Snoopy', 30)]
scores = { key: value for key, value in data }

In each of these cases, the names are keys and numbers are values.

There’s one big restriction to the keys of dictionaries: they need to be hashable:

An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() method). Hashable objects which compare equal must have the same hash value.

So you can’t use a list as a key for example because it’s a mutable one.

Accessing values

Regardless of what was used to create it, we can access the values in a couple of ways:

scores = { 'Charlie': 10, 'Patty': 25, 'Snoopy': 30 }

# With brackets
scores['Charlie'] # == 10

# With .get()
scores.get('Charlie') # == 10

The difference between these two is what happens when a key does not exist.

scores['Linus']  # raises KeyError: 'Linus'
scores.get('Linus')  # is None

# We can give .get() a default value
scores.get('Linus', 0)  # == 0

Changing values

Dictionaries are mutable data structures which means you can change their data.

# Set new value
scores['Charlie'] = 15

# Modify a value
scores['Charlie'] += 5

# Delete a key
del scores['Charlie']

Since there is a direct “one key to one value” relationship, dictionaries are very handy in collecting data for or counting values belonging to this key.

Let’s say we have a dataset where each time someone scores a point, their name is listed. We then want to count how may points each has:

marks = [
  'Charlie', 'Charlie', 'Patty', 
  'Snoopy', 'Snoopy', 'Charlie', 
  'Snoopy', 'Snoopy'
]

scores = {} # Creating an empty dict
for person in marks: # Go through every mark
  if person not in scores: # If it's not in the dict yet,
    scores[person] = 0 # create an entry with starting value 0
  
  scores[person] += 1 # Add 1 point to this person
  
print(scores)
# {'Charlie': 3, 'Patty': 1, 'Snoopy': 4}

We’ll look a bit later how we can improve this with some of the special dictionaries but this basic structure of turning data into a dictionary is a fundamental basic to learn in Python.

While the key needs to be hashable, the values can be anything. So instead of just increasing a number from zero upwards, we could store data as a list or another dictionary or whatever.

Looping over

# By default, loops over keys in for-in
for person in scores:
  print(person, scores[person])
  
# Loop over values
for score in scores.values():
  print(score)
  
# Loop over both
for person, score in scores.items():
  print(person, score)

Sorting a dictionary

On a concept level, basic mapping does not have an order. In Python, dictionaries maintain the order the keys are inserted in and there are ways to force an order (like OrderedDict) but it’s a good baseline to base your knowledge of dictionaries on.

However, when looping over a dictionary, you often want the data to be in some sort of order. In the earlier example when we counted marks to a dictionary, we might want to print it in an order of most points to least.

For this, we can use sorted function:

# Let's recreate our dictionary
scores = {'Charlie': 3, 'Patty': 1, 'Snoopy': 4}

for person in sorted(scores, key=scores.get, reverse=True):
  print(f'{person}: {scores[person]}')
  
# Snoopy: 4
# Charlie: 3
# Patty: 1

The first argument to sorted is in this case our dictionary. The second key, key= is a single argument function that defines what is used for sorting (in this case, scores.get tells the function we want the values corresponding to the keys) and reverse= can be used to reverse the order from ascending to descending.

It’s important to note that nothing internally in the dictionary changes when using sorted. It returns a new list with the keys sorted based on the sorting criteria.

You’ll do just fine with dictionary for a long time

Next, I’ll introduce some specialised dictionaries that are included in the standard library because developers have found these cases very useful. If you’re a new developer, I do recommend focusing on using and understanding the basic dictionary.

There’s nothing in the following dictionaries that you can’t do in the basic one.

Brian Holt once said this in one of his containers course:

When I don’t know how my tools work, I tend to resent them because they add complexity to my life.

When I understand what they are doing for me and what I no longer have to do because the tool is doing it for me, I tend to really like my tools.

Jumping into the more advanced use cases before understanding the basics really well can be detrimental. Finish this blog post to see what’s out there but don’t be afraid to manually write the code they would let you skip over and over again so that you’ll gain a deeper understanding.

defaultdict

In the earlier example of creating and populating a dictionary, we had to check if a key existed before we could interact with it:

marks = [
  'Charlie', 'Charlie', 'Patty', 
  'Snoopy', 'Snoopy', 'Charlie', 
  'Snoopy', 'Snoopy'
]

scores = {} # Creating an empty dict
for person in marks: # Go through every mark
  if person not in scores: # If it's not in the dict yet,
    scores[person] = 0 # create an entry with starting value 0
  
  scores[person] += 1 # Add 1 point to this person
  
print(scores)
# {'Charlie': 3, 'Patty': 1, 'Snoopy': 4}

There’s a way to shortcut this by using a defaultdict:

from collections import defaultdict

marks = [
  'Charlie', 'Charlie', 'Patty', 
  'Snoopy', 'Snoopy', 'Charlie', 
  'Snoopy', 'Snoopy'
]

# Create a new dictionary with
# default value of 0 for each key
scores = defaultdict(int) 

for person in marks:
  scores[person] += 1
  
print(scores)
# defaultdict(<class 'int'>, {'Charlie': 3, 'Patty': 1, 'Snoopy': 4})

Passing int to defaultdict tells the dictionary to start from a default value of 0. You could also pass list and the default would be an empty list. As you can see comparing the two examples above, the code becomes way easier to read and comprehend.

Counter

Our example of counting things is such a common operation that there’s a special Counter dictionary for it.

from collections import Counter

marks = [
  'Charlie', 'Charlie', 'Patty', 
  'Snoopy', 'Snoopy', 'Charlie', 
  'Snoopy', 'Snoopy'
]

scores = Counter(marks)

print(scores)
# Counter({'Snoopy': 4, 'Charlie': 3, 'Patty': 1})

Not only does Counter help us in creation of it but it has a couple of really handy methods.

I have written about Counter before and rather than repeating myself here, I recommend reading through that post.


If something above resonated with you, let's start a discussion about it! Email me at juhis@hamatti.org and share your thoughts. This year, I want to have more deeper discussions with people from around the world and I'd love if you'd be part of that.