Python Dictionary Basics

Chapter 27 16 mins

Learning outcomes:

  1. Creating a dictionary
  2. Adding and removing items
  3. Checking for a key, a value or a key-value pair
  4. Coercing a dictionary into a Boolean
  5. The dict() method

Introduction

So far in this course we've covered a decent amount of data types in Python including numbers, strings, Booleans, lists, and sets. Each type has a specific purpose in the language and its own set of functionality and notations.

In this chapter, we shall take a look over another such data type - dictionaries. Dictionaries in Python are based on the concept of dictionaries in the real world.

We have word dictionaries where there are a huge number of words each followed by its meaning. The same idea works in Python.

Going with the real world analogy, the words are referred to as keys whereas their meanings are referred to as values of these keys. One looks into a dictionary via a key, and once the key is found, its value is what becomes of concern.

This concept of key-value pairs is not an invention of Python. It was there, and is still there in many other languages as well. PHP implements this as associative arrays, JavaScript implements this as objects, C implements this as hash tables and so on.

Python just stands out by using a very familiar and sensible term to refer to this data structure i.e dictionaries.

Creating a dictionary

As with most data types, there is a literal way of creating a dictionary in Python. That is using a pair of {} curly braces.

The general syntax of a dictionary literal is shown below:

{key1: value1, key2: value2}

We start with a pair of {} curly braces. Inside these, first comes the key and then its value, separated by a : colon. This is collectively referred to as a key-value pair.

Different key-value pairs are separated by a , comma.

Dictionary keys can either be strings, numbers or tuples. All these three data types are hashable and can therefore be used validly as dictionary keys.

We'll see later on as to why do the keys of a dictionary need to be hashable.

Usually it's the string data type that's used as dictionary keys, since we can name them any way we like. Numbers are less common because using them gives the impression of rather working with a list; and tuples are useful only in rare cases. So technically what we are left with is mainly strings.

Anyways, let's get our hands on the syntax above by constructing a simple dictionary

Below we create a dictionary called thesaurus and put a couple of words from English language in it as keys along with their synonyms as values of the keys.

thesaurus = {
    'good': 'splendid, super, great',
    'bad': 'poor, faulty, imperfect',
    'small': 'little, miniature, tiny'
}

To get the value of a given key, we need to use bracket notation as shown below:

thesaurus['small']
'little, miniature, tiny'

First comes the dictionary followed by a pair of [] square brackets and then within it the key whose value we want to retrieve.

If the key's a string, we give it as a string, if it's a number, it's given as a number.

Consider the code below where we create a dictionary d with two keys: one of type string and one of type number:

d = {'x': 'Hello', 10: 'World!'}
d['x']
'Hello'
d[10]
'World!'
d['10']
Traceback (most recent call last): File "<pyshell#3>", line 1, in d['10'] KeyError: '10'

As you can clearly see, accessing the numeric key using a stringified value is invalid, even though the string does represent the numeric key.

Adding keys

Adding keys to a dictionary is not difficult at all. We have to use the same old bracket notation in an assignment context.

Below we add a new word 'big' to our dictionary thesaurus:

thesaurus = {
    'good': 'splendid, super, great',
    'bad': 'poor, faulty, imperfect',
    'small': 'little, miniature, tiny'
}

# add a key 'big'
thesaurus['big'] = 'large, huge, enormous'

print(thesaurus)
{'good': 'splendid, super, great', 'bad': 'poor, faulty, imperfect', 'small': 'little, miniature, tiny', 'big': 'large, huge, enormous'}

Keep in mind that if the key already exists, then this assignment will override the current value of the key.

Consider the code below:

thesaurus = {
    'good': 'splendid, super, great',
    'bad': 'poor, faulty, imperfect',
    'small': 'little, miniature, tiny'
}

thesaurus['small'] = 'minute, insignificant, short'

The statement here doesn't add a new key 'small' to the dictionary thesaurus; in fact, 'small' already exists in there. Instead, the statement just overrides the previous value 'little, miniature, tiny' with 'minute, insignificant, short'.

Now if we inspect the key in the shell, we get the latest value returned:

thesaurus['small']
'minute, insignificant, short'

Removing items

Removing items from a dictionary follows the same rules as removing elements from a list. We can use the del keyword to delete a key-value pair from a dictionary by specifying the dictionary and its key in bracket notation.

Once a key is deleted, it no longer exists in a dictionary, and hence referring to it throws a KeyError exception.

Consider the code below:

thesaurus = {
    'good': 'splendid, super, great',
    'bad': 'poor, faulty, imperfect',
    'small': 'little, miniature, tiny'
}

# delete the key 'small'
del thesaurus['small']

print(thesaurus['small'])
Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> d['d'] KeyError: 'd'

Checking for a key

Say you've created a dictionary and want to check somewhere in your program whether a given key exists in it or not. How to do this? Well we can use our old friend — the in operator.

When the in operator is invoked on a dictionary, it searches across all the keys of the dictionary, returning True if the given key exists; or otherwise False.

An example follows:

thesaurus = {
    'good': 'splendid, super, great',
    'bad': 'poor, faulty, imperfect',
    'small': 'little, miniature, tiny'
}
'small' in thesaurus
True
'SMALL' in thesaurus
False

The key 'small' exists in thesaurus and likewise we get True returned in the first statement. However, there is no such key as 'SMALL' in the dictionary and likewise False is returned.

Remember that dictionary keys are case-sensitive.

Internally, the in operator searches for the given key in dict.keys(). dict.keys() returns a sequence holding all the keys of dict.

We shall understand the keys() method in detail in the next chapter.

Checking for a value

Ocassionally, instead of checking whether a given key exists in a dictionary. it's required to check whether a given value exists in it.

To accomplish this we can use the same old in operator but not on the dictionary itself, rather on the sequence obtained by calling the values() method on the dictionary.

The values() method operates similar to keys() except for that it returns a sequence of all values in a dictionary.

Below shown is an example using the same old thesaurus dictionary:

thesaurus = {
    'good': 'splendid, super, great',
    'bad': 'poor, faulty, imperfect',
    'small': 'little, miniature, tiny'
}
'splendid, super, great' in thesaurus.values()
True
'splendid' in thesaurus.values()
False

We can further inspect values() by converting it into a list using the global list() function.

list(thesaurus.values())
['splendid, super, great', 'poor, faulty, imperfect', 'little, miniature, tiny']

As can be seen, thesaurus.values() holds all the values of the dictionary as a sequence.

We'll explore the values() method in detail in the next chapter.

Checking for a key-value pair

Python is a highly flexible and feature-full language. It just offers everything that a developer would ever want to have in a programming language.

We've seen above how to check for the existence of a given key or a value in a dictionary. The in operator when used on a dictionary d (or analogously, on d.keys()) tells whether or not a given key exists in it, whereas when used on d.values() it tells whether or not a given value exists.

However, what if we want to check whether a specific key exists with a specific value? That is, what if we want to check for a complete key-value pair?

Fortunately, Python offers the items() method to help us out.

The items() method returns a sequence of all the key-value pairs in a dictionary. Each pair is given as a tuple of two elements: first one the key, and the second one the value.

Consider the following code:

a = {'x': 1, 'y': 2, 'z': 10}
print(a.items())
dict_items([('x', 1), ('y', 2), ('z', 10)])

Based on this, to search for a key-value pair within the items() sequence, we first have to specify the pair in the format of a tuple and then use the in operator to find this tuple in the sequence.

An example follows:

a = {'x': 1, 'y': 2, 'z': 10}
('x', 1) in a.items()
True
('y', 2) in a.items()
True
('z', '10') in a.items()
False
We'll explore the items() method in detail in the next chapter.

Coercion to Boolean

As with empty strings, lists, tuples, and empty sets, empty dictionaries also convert to the Boolean value False.

Based on this coercion one can simply refer to a dictionary directly in a conditional statement to check whether it is empty or not.

Below we add a key x to the dictionary d only if it is empty; otherwise print 'Non-empty!'

d = {}

if not d:
    d['x'] = 'Hello World!'
else:
    print('Non-empty')

Although it's clear but recapping it, a non-empty dictionary coerces into the Boolean value True.

The dict() method

Creating dictionaries isn't just limited to curly braces. One can also use the dict() function to define a dictionary, and that in many ways:

  1. With no arguments
  2. With an iterable sequence as argument
  3. With a mapping object as argument
  4. By specifying keyword arguments

Let's explore all these ways...

No arguments

Calling the dict() function without any arguments results in the creation of an empty dictionary. Keys can be subsequently added using bracket notation.

Consider the snippet shown below:

d = dict() # creates an empty dictionary

print(d)

d['x'] = 1
d['y'] = 2
d['z'] = 10

print(d)
{}
{'x': 1, 'y': 2, 'z': 10}
If an empty dictionary is to be created, it's more convenient to use the dictionary literal {}, rather than the dict() function.

An iterable argument

The dict() function can also be called with an iterable sequence as argument.

In this case each element of the sequence represents a key-value pair in the dictionary being created. This implies that each element must be an iterable sequence itself with a length of 2.

The first item of each sequence is made the key whereas the second item is made its corresponding value.

Shown below is an example:

l = [ ('x', 1), ('y', 2), ('z', 10) ]
d = dict(l)

print(d) # {'x': 1, 'y': 2, 'z': 10}

print(d['x']) # 1
print(d['y']) # 2
{'x': 1, 'y': 2, 'z': 10}
1
2

Here we pass a list l to the dict() function. Each element of l is a tuple of length 2. The first item of each tuple is made a key of the dictionary while the second item is made its corresponding value.

Note that it's not at all necessary for each element of the given iterable to be a tuple, as shown above. It could be any iterable, such as a list.

Below, we rewrite the code above using lists inside l, instead of tuples:

l = [ ['x', 1], ['y', 2], ['z', 10] ]
d = dict(l)

print(d)

print(d['x'])
print(d['y'])
{'x': 1, 'y': 2, 'z': 10}
1
2

If each element of the iterable sequence passed to dict() is not an iterable, Python throws a TypeError; as it's the type of value that is invalid here.

d = dict([10, 20, True])
Traceback (most recent call last): File "stdin", line 1, in <module> d = dict([10, 20, True]) TypeError: cannot convert dictionary update sequence element #0 to a sequence

Moreover, if each element is an iterable but does not have a length of 2, Python throws a ValueError; as this time, it's the value of the iterable that's at problem here.

d = dict([ ('x', 1, 2), ('y', 2), ('z', 10) ])
Traceback (most recent call last): File "stdin", line 1, in <module> d = dict([ ('x', 1, 2), ('y', 2), ('z', 10) ]) ValueError: dictionary update sequence element #0 has length 3; 2 is required

A mapping object

If a mapping object, such as an existing dictionary, is passed to the dict() function, it creates a new dictionary out of it.

This means that to copy a dictionary we can pass it to the dict() function.

Consider the code below:

d1 = {'x': 1, 'y': 2}
d2 = dict(d1)

print(d1['x']) # 1
print(d2['x']) # 1

# changing d1 doesn't change d2
d1['x'] = 100

print(d1['x']) # 100
print(d2['x']) # 1

First we create a dictionary d1 and then create a copy of it by passing it to dict(). This copy is saved in d2. To confirm that d2 is a copy of d1 (and not holding the same reference) we change the value of d1['x'] and then inspect both dictionaries' x key.

As expected, d1 has changed (in line 10), however d2 remains unchanged (in line 11).

Keyword arguments

A dictionary can also be created by sending keyword arguments to the dict() function.

The name of the argument becomes a key of the dictionary and its value becomes the corresponding value of the key.

Consider the code below:

# passing keyword arguments
d = dict(x=1, y=2, z=10)

print(d)
{'x': 1, 'y': 2, 'z': 10}

Although it's uncommon to create dictionaries using keyword arguments, it's still good to know that this is possible, if ever you need to create dictionaries this way. Who knows?