The world’s leading publication for data science, AI, and ML professionals.

Dictionaries in Python

One of the most important data structures in Python

Photo by Mathilde Decourcelle on Unsplash
Photo by Mathilde Decourcelle on Unsplash

In this article, I will talk about dictionaries. This is the second article in the series named "Data Structures in Python". The first part of this series was about lists.

Dictionaries are important data structures in Python that use keys for indexing. They are an unordered sequence of items (key-value pairs), which means the order is not preserved. The keys are immutable. Just like lists, the values of dictionaries can hold heterogeneous data i.e., integers, floats, strings, NaN, Booleans, lists, arrays, and even nested dictionaries.

This article will provide you a clear understanding and enable you to work proficiently with Python dictionaries.

The following topics are covered in this article:

  • Creating a dictionary and adding elements
  • Accessing dictionary elements
  • Removing dictionary elements
  • Adding/inserting new elements
  • Merging/concatenating dictionaries
  • Modifying a dictionary
  • Sorting a dictionary
  • Dictionary comprehension
  • Alternative ways to create dictionaries
  • Copying a dictionary
  • Renaming existing keys
  • Nested dictionaries
  • Checking if a key exists in a dictionary

1) Creating a dictionary and adding elements

Like lists are initialized using square brackets ([ ]), dictionaries are initialized using curly brackets ({ }). An empty dictionary has, of course, zero length.

dic_a = {} # An empty dictionary
type(dic_a)
>>> dict
len(dic_a)
>>> 0

A dictionary has two characteristic features: keys and values. Each key has a corresponding value. Both, the key and the value, can be of type string, float, integer, NaN, etc. Adding elements to a dictionary means adding a key-value pair. A dictionary is composed of one or more key-value pairs.

Let us add some elements to our empty dictionary. The following is one way to do so. Here, 'A' is the key and 'Apple' is its value. You can add as many elements as you like.

# Adding the first element
dic_a['A'] = 'Apple'
print (dic_a)
>>> {'A': 'Apple'}
# Adding the second element
dic_a['B'] = 'Ball'
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball'}

Note: Python being case-sensitive, 'A' and 'a' act as two different keys.

dic_a['a'] = 'apple'
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'a': 'apple'}

Initializing dictionary all at once

If you find the above method of adding elements one-by-one tiresome, you can also initialize the dictionary at once by specifying all the key-value pairs.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

Heterogeneous dictionary

So far your dictionary had strings as keys and values. A dictionary can also store data of mixed types. The following is a valid Python dictionary.

dic_b = {1: 'Ace', 'B': 123, np.nan: 99.9, 'D': np.nan, 'E': np.inf}

You should though use meaningful names for the keys as they denote the indices of dictionaries. In particular, avoid using floats and np.nan as keys.


2) Accessing dictionary elements

Having created our dictionaries, let us see how we can access their elements.

Accessing the keys and values

You can access the keys and values using the functions dict.keys() and dict.values(), respectively. You can also access both keys and values in the form of tuples using the items() function.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.keys()
>>> dict_keys(['A', 'B', 'C'])
dic_a.values()
>>> dict_values(['Apple', 'Ball', 'Cat'])
dic_a.items()
>>> dict_items([('A', 'Apple'), ('B', 'Ball'), ('C', 'Cat')])

Alternatively, you can also use a "for" loop to access/print them one at a time.

# Printing keys
for key in dic_a.keys():
    print (key, end=' ')
>>> A B C
#############################
# Printing values
for key in dic_a.values():
    print (key, end=' ')
>>> Apple Ball Cat

You can avoid two "for" loops and access the keys and values using items(). The "for" loop will iterate through the key-value pairs returned by items(). Here, the key and value are arbitrary variable names.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
for key, value in dic_a.items():
    print (key, value)
>>> A Apple
    B Ball
    C Cat

Accessing individual elements

The dictionary items cannot be accessed using list-like indexing.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a[0]
>>> ----> 1 dic_a[0]
    KeyError: 0

You need to use the keys to access the corresponding values from a dictionary.

# Accessing the value "Apple"
dic_a['A']
>>> 'Apple'
# Accessing the value "Cat"
dic_a['C']
>>> 'Cat'

You will get an error if the key does not exist in the dictionary.

dic_a['Z']
>>> KeyError Traceback (most recent call last)
----> 1 dic_a['Z']
KeyError: 'Z'

If you want to avoid such key errors in case of non-existent keys, you can use the get() function. This returns None when the key does not exist. You can also use a custom message to be returned.

print (dic_a.get('Z'))
>>> None
# Custom return message
print (dic_a.get('Z', 'Key does not exist'))
>>> Key does not exist

Accessing dictionary elements like in a list

If you want to access dictionary elements (either keys or values) using indices, you need to first convert them into lists.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
list(dic_a.keys())[0]
>>> 'A'
list(dic_a.keys())[-1]
>>> 'C'
list(dic_a.values())[0]
>>> 'Apple'
list(dic_a.values())[-1]
>>> 'Cat'

3) Removing dictionary elements

Deleting elements from a dictionary means deleting a key-value pair together.

Using del

You can delete the dictionary elements using the del keyword and the key whose value you want to delete. The deletion is in-place, which means you do not need to re-assign the value of the dictionary after deletion.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
# Deleting the key-value pair of 'A': 'Apple'
del dic_a['A']
print (dic_a)
>>> {'B': 'Ball', 'C': 'Cat'}
# Deleting the key-value pair of 'C': 'Cat'
del dic_a['C']
print (dic_a)
>>> {'B': 'Ball'}

Using pop( )

You can also use the "pop( )" function to delete elements. It returns the value being popped (deleted) and the dictionary is modified in-place.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.pop('A')
>>> 'Apple' 
print (dic_a)
# {'B': 'Ball', 'C': 'Cat'}

In both the above-described methods, you will get a KeyError if the key to be deleted does not exist in the dictionary. In the case of "pop( )", you can specify the error message to show up if the key does not exist.

key_to_delete = 'E'
dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.pop(key_to_delete, f'Key {key_to_delete} does not exist.')
>>> 'Key E does not exist.'

Deleting multiple elements

There is no direct way but you can use a "for" loop as shown below.

to_delete = ['A', 'C']
dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
for key in to_delete:
    del dic_a[key]

print (dic_a)
>>> {'B': 'Ball'}

4) Adding/inserting new elements

You can add one element at a time to an already existing dictionary as shown below.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a['D'] = 'Dog'
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
dic_a['E'] = 'Egg'
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog', 'E': 'Egg'}

If the key you are adding already exists, the existing value will be overwritten.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a['A'] = 'Adam' # key 'A' already exists with the value 'Apple'
print (dic_a)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat'}

Using update( )

You can also use the update() function for adding a new key-value pair by passing the pair as an argument.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.update({'D': 'Dog'})
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

The update() function also allows you to add multiple key-value pairs simultaneously to an existing dictionary.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = {'D':'Dog', 'E':'Egg'}
dic_a.update(dic_b)
print(dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog', 'E': 'Egg'}

5) Merging/concatenating dictionaries

You can merge two or more dictionaries using the unpacking operator (**) starting Python 3.5 onwards.

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}
dic_merged = {**dic_a, **dic_b}
print (dic_merged)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

If you don’t want to create a new dictionary but just want to add dic_bto the existing dic_a, you can simply update the first dictionary as shown earlier.

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}
dic_a.update(dic_b)
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

How are duplicate keys handled while concatenating?

One of the characteristics of Python dictionaries is that they cannot have duplicate keys i.e., a key cannot appear twice. So, what happens if you concatenate two or more dictionaries that have one or more common keys.

The answer is that the key-value pair in the last merged dictionary (in the order of merging) will survive. In the following example, the key'A' exists in all three dictionaries, and, therefore, the final dictionary took the value from the last merged dictionary (dic_c).

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'A': 'Apricot'}
dic_c = {'A': 'Adam', 'E': 'Egg'}
dic_merged = {**dic_a, **dic_b, **dic_c}
print (dic_merged)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat', 'E': 'Egg'}

Word of caution

I just said that the dictionaries cannot have duplicate keys. Strictly speaking, you can define a dictionary with duplicate keys, but, when you print it, only the last duplicate key will be printed. A shown below, only unique keys are returned, and for the duplicated key (‘A’ here), only the last value is returned.

dic_a = {'A': 'Apple', 'B': 'Ball', 'A': 'Apricot', 'A': 'Assault'}
print (dic_a)
>>> {'A': 'Assault', 'B': 'Ball'}

Easier way in Python 3.9+

From Python 3.9 onwards, you can use the | operator to concatenate two or more dictionaries.

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}
dic_c = dic_a | dic_b
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
# Concatenating more than 2 dictionaries
dic_d = dic_a | dic_b | dic_c

6) Modifying a dictionary

If you want to change the value of 'A' from 'Apple' to 'Apricot', you can use a simple assignment.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a['A'] = 'Apricot'
print (dic_a)
>>> {'A': 'Apricot', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

7) Sorting a dictionary

The order is not maintained in a dictionary. You can sort a dictionary using either the keys or the values using the sorted() function.

Sorting using keys

If the keys are strings (alphabets), they will be sorted alphabetically. In a dictionary, we have two main elements: keys and values. Hence, while sorting with respect to the keys, we use the first element i.e. keys, and, therefore, the index used in the lambda function is "[0]". You can read this post for more information on the lambda functions.

dic_a = {'B': 100, 'C': 10, 'D': 90, 'A': 40}
sorted(dic_a.items(), key=lambda x: x[0])
>>> [('A', 40), ('B', 100), ('C', 10), ('D', 90)]

The sorting is not in-place. As shown below, if you now print the dictionary, it remains unordered, as initialized originally. You will have to reassign it after sorting.

# The dictionary remains unordered if you print it
print (dic_a)
>>> {'B': 100, 'C': 10, 'D': 90, 'A': 40}

If you want to sort in reverse order, specify the keyword reverse=True.

sorted(dic_a.items(), key=lambda x: x[0], reverse=True)
>>> [('D', 90), ('C', 10), ('B', 100), ('A', 40)]

Sorting using values

To sort a dictionary based on its values, you need to use the index "[1]" inside the lambda function.

dic_a = {'B': 100, 'C': 10, 'D': 90, 'A': 40}
sorted(dic_a.items(), key=lambda x: x[1])
>>> [('C', 10), ('A', 40), ('D', 90), ('B', 100)]

8) Dictionary comprehension

It is a very helpful method for creating dictionaries dynamically. Suppose you want to create a dictionary where the key is an integer and the value is its square. A dictionary comprehension would look like the following.

dic_c = {i: i**2 for i in range(5)}
print (dic_c)
>>> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

If you want your keys to be strings, you can use "f-strings".

dic_c = {f'{i}': i**2 for i in range(5)}
print (dic_c)
>>> {'0': 0, '1': 1, '2': 4, '3': 9, '4': 16}

9) Alternative ways to create dictionaries

Creating a dictionary from lists

Suppose you have two lists and you want to create a dictionary out of them. The simplest way is to use the dict() constructor.

names = ['Sam', 'Adam', 'Tom', 'Harry']
marks = [90, 85, 55, 70]
dic_grades = dict(zip(names, marks))
print (dic_grades)
>>> {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}

You can also zip the two lists together and create the dictionary using "dictionary comprehension" as shown earlier.

dic_grades = {k:v for k, v in zip(names, marks)}
print (dic_grades)
>>> {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}

Passing key-value pairs

You can also pass a list of key-value pairs separated by commas to the dict() construct and it will return a dictionary.

dic_a = dict([('A', 'Apple'), ('B', 'Ball'), ('C', 'Cat')])
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

If your keys are strings, you can use even a simpler initialization using only the variables as the keys.

dic_a = dict(A='Apple', B='Ball', C='Cat')
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

10) Copying a dictionary

I will explain this point using a simple example. There are more subtleties involved in the copying mechanism of dictionaries, and, I would recommend the reader to refer to this Stack Overflow post for a detailed explanation.

Reference assignment

When you simply reassign an existing dictionary (parent dictionary) to a new dictionary, both point to the same object ("reference assignment").

Consider the following example where you reassign dic_a to dic_b.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = dic_a # Simple reassignment (Reference assignment)

Now, if you modify dic_b(for e.g. adding a new element), you will notice that the change will also be reflected in dic_a.

dic_b['D'] = 'Dog'
print (dic_b)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

Shallow Copy

A shallow copy is created using the copy() function. In a shallow copy, the two dictionaries act as two independent objects, with their contents still sharing the same reference. If you add a new key-value pair in the new dictionary (shallow copy), it will not show up in the parent dictionary.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = dic_a.copy()
dic_b['D'] = 'Dog'
# New, shallow copy, has the new key-value pair
print (dic_b)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
# The parent dictionary does not have the new key-value pair
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

Now, if the contents in the parent dictionary ("dic_a") will change depends on the type of the value. For instance, in the following, the contents are simple strings that are immutable. So changing the value in "dic_b" for a given key ('A'in this case) will not change the value of the key 'A' in "dic_a".

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = dic_a.copy()
# Replace an existing key with a new value in the shallow copy
dic_b['A'] = 'Adam'
print (dic_b)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat'}
# Strings are immutable so 'Apple' doesn't change to 'Adam' in dic_a
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

However, if the value of the key 'A' in "dic_a" is a list, then changing its value in the "dic_b" will reflect the changes in "dic_a" (parent dictionary), because lists are mutable.

dic_a = {'A': ['Apple'], 'B': 'Ball', 'C': 'Cat'}
# Make a shallow copy
dic_b = dic_a.copy()
dic_b['A'][0] = 'Adam'
print (dic_b)
>>> {'A': ['Adam'], 'B': 'Ball', 'C': 'Coal'}
# Lists are mutable so the changes get reflected in dic_a too
print (dic_a)
>>> {'A': ['Adam'], 'B': 'Ball', 'C': 'Cat'}

11) Renaming existing keys

Suppose you want to replace the key "Adam" by "Alex". You can use the pop function because it deletes the passed key ("Adam" here) and returns the deleted value (85 here). So you kill two birds with a single shot. Use the returned (deleted) value to assign the value to the new key ("Alex" here). There can be more complicated cases where the key is a tuple. Such cases are out of the scope of this article.

dic_a = {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}
dic_a['Alex'] = dic_a.pop('Adam')
print (dic_a)
>>> {'Sam': 90, 'Tom': 55, 'Harry': 70, 'Alex': 85}

12) Nested dictionaries

A nested dictionary has one or more dictionaries within a dictionary. The following is the simplest example of a nested dictionary with two layers of nesting. Here, the outer dictionary (layer 1) has only one key-value pair. However, the value is now a dictionary itself.

dic_a = {'A': {'B': 'Ball'}}
dic_a['A']
>>> {'B': 'Ball'}
type(dic_a['A'])
>>> dict

If you want to further access the key-value pair of the inner dictionary (layer 2), you now need to use dic_a['A'] as the dictionary.

dic_a['A']['B']
>>> 'Ball'

Three-layered dictionary

Let us add an additional layer of the nested dictionary. Now, dic_a['A'] is a nested dictionary in itself, unlike the simplest nested dictionary above.

dic_a = {'A': {'B': {'C': 'Cat'}}}
# Layer 1
dic_a['A']
>>> {'B': {'C': 'Cat'}}
# Layer 2
dic_a['A']['B']
>>> {'C': 'Cat'}
# Layer 3
dic_a['A']['B']['C']
>>> 'Cat'

13) Checking if a key exists in a dictionary

You can find if a particular key exists in a dictionary using the in operator.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
'A' in dic_a
# True
'E' in dic_a
# False

In the above code, you do not need to use "in dic_a.keys( )" because "in dic_a" already looks up in the keys.


This brings me to the end of this article. You can access the first part of this series on "Data Structures in Python" here.


Related Articles