Readings: dictionaries
Contents
6.2. Readings: dictionaries#
6.2.1. Dictionaries#
Dictionaries hold collections of values in key-value pairs. In a dictionary each key must be unique, but values can be repeated. An example of a dictionary might be a map between a chemical element letter and the chemical element’s name. The corresponding data-type in Python is dict
.
6.2.1.1. Create a dictionary#
The syntax for creating a dictionary is as follows:
dictionary_name = {key1:value1, key2:value2....}
As stated previously, keys have to be unique and immutable. This means that we can use data types such as strings, integers, floats, tuples, etc as keys. Values can be of any type and can repeat as well. For example:
chemical_elements = {
'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H':'Hydrogen'
}
chemical_elements
{'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen'}
As you can see, chemical_elements
is an example of a dictionary that maps the chemical symbol to the name of the element. The keys are strings and are immutable. In this case the values happen to be strings and as a result they are immutable, but this is not necessarily always the case.
Consider a parabola. As you might now, it is symmetric with respect to the y-axis. So for the same value of the function, there will be two values of x. The keys of our dictionary will be the x-values and the values will be the y-values. Here is an example:
parabola = {0:0, 1:1, -1:1, 2:4, -2:4}
parabola
{0: 0, 1: 1, -1: 1, 2: 4, -2: 4}
To access a single element we need to use the key. The syntax is as follows:
dictionary_name[key]
For example:
print(chemical_elements['O'])
print(parabola[1])
Oxygen
1
Note
If we try to access the value of a key that does not exist, we will get a KeyError
.
chemical_elements['Ca']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
/var/folders/7f/7nw_x13n5q965rss_qz6061m0000gq/T/ipykernel_60210/525199925.py in <module>
----> 1 chemical_elements['Ca']
KeyError: 'Ca'
To prevent such errors, it is desirable to use the get()
method of dictionaries. If the key exists, it returns the value associated with that key, otherwise it returns None
. To return another message we can pass the message as a second argument to the get()
method:
chemical_elements.get('Ca', 'Not present')
'Not present'
6.2.1.2. Adding key-value pairs#
We can add a key-value pair for a key that does not already exist in a dictionary as follows:
dictionary_name[new_key] = value
For example, if we want to add Potassium
to the chemical_elements
dictionary then we would write:
chemical_elements['K'] = 'Potassium'
chemical_elements
{'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H': 'Hydrogen',
'K': 'Potassium'}
6.2.1.3. Removing key-value pairs#
In order to remove a key-value pair from a dictionary we need to use the del operator:
del dictionary_name[key]
del chemical_elements['K']
chemical_elements
{'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen'}
Another way to remove key-value pairs is by using the pop(key_name)
or popitem()
methods. The first one removes key-value pair that corresponds to the passed key_name
argument, while the second one removes the last inserted key-value pair.
chemical_elements['Ca'] = 'Calcium'
chemical_elements['K'] = 'Potassium'
chemical_elements
{'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H': 'Hydrogen',
'Ca': 'Calcium',
'K': 'Potassium'}
chemical_elements.pop('Ca')
chemical_elements
{'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H': 'Hydrogen',
'K': 'Potassium'}
chemical_elements.popitem()
chemical_elements
{'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen'}
If we want to empty the dictionary we can use the clear()
method. Mind that this will not delete the dictionary, but it will delete all key-value pairs.
print('Before clearing:', parabola)
parabola.clear()
print('After clearing:', parabola)
Before clearing: {0: 0, 1: 1, -1: 1, 2: 4, -2: 4}
After clearing: {}
6.2.1.4. Updating key-value pairs#
In order to update a value related to a key we just access the value corresponding to that key and assign a new value. The syntax is as follows:
dictionary_names[key] = new_value
For example, we can add again ‘Potasium’ to the list, misspelled, and then change it to the correct version:
chemical_elements['K'] = 'Potasium' # add Potassium again
chemical_elements
{'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H': 'Hydrogen',
'K': 'Potasium'}
chemical_elements['K'] = 'Potassium' # correct spelling
chemical_elements
{'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H': 'Hydrogen',
'K': 'Potassium'}
6.2.1.5. Accessing keys, values and key-value pairs#
In order to get keys, values and key-value pairs of a dictionary we need to use the respective methods: keys()
, values()
and items()
. All these methods return views of the items retrieved. This means that each time we invoke them, the values from the dictionary will be returned and the return is not saved anywhere. To better understand this, look at this example:
6.2.1.5.1. Accessing keys#
chemical_keys = chemical_elements.keys()
chemical_keys
dict_keys(['O', 'C', 'Na', 'H', 'K'])
chemical_elements['Ca'] = 'Calcium'
chemical_keys
dict_keys(['O', 'C', 'Na', 'H', 'K', 'Ca'])
First of all, as you can see the return type is dict_keys
. The second thing to note is that, despite that we do not update the values of the variable chemical_keys
, it implicitly is updated and contains the new value. This is the role of a view, it gives a view of the keys and it does not keep a deep copy of the keys.
6.2.1.5.2. Accessing values#
Similarly, to access the values we can use the values()
method. Again it will return a view of the dictionary values:
chemical_values = chemical_elements.values()
chemical_values
dict_values(['Oxygen', 'Carbon', 'Sodium', 'Hydrogen', 'Potassium', 'Calcium'])
Again, it returns a dict_values
type.
To ensure that there is no copy of data saved in chemical_values
, we will delete Calcium
:
del chemical_elements['Ca']
chemical_values
dict_values(['Oxygen', 'Carbon', 'Sodium', 'Hydrogen', 'Potassium'])
As you can see, there is no Calcium
anymore in the values of the dictionary.
6.2.1.5.3. Accessing key-value pairs#
To access key-value
pairs we can use the items()
method. Again it will return a view.
chemical_pairs = chemical_elements.items()
chemical_pairs
dict_items([('O', 'Oxygen'), ('C', 'Carbon'), ('Na', 'Sodium'), ('H', 'Hydrogen'), ('K', 'Potassium')])
6.2.1.6. Searching dictionaries for specific keys#
In order to check whether a certain key is in the dictionary we can use the in
or not in
operators:
'Ca' in chemical_elements
False
'Ca' not in chemical_elements
True
As you can see, since there is no entry in the dictionary whose key is Ca
, the output is False
. If the key is present then it would output True
.
'Na' in chemical_elements
True
'Na' not in chemical_elements
False
6.2.1.7. update() method#
To add or update a key-value pair in a dictionary the update()
method of dictionaries can be used as well. The syntax is:
dictionary_names.update({key:value})
As previously, if the key does not exist in the dictionary, then the key-value pair is added, otherwise the value is simply updated.
chemical_elements.update({'N':'Nitrog.'})
chemical_elements
{'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H': 'Hydrogen',
'K': 'Potassium',
'N': 'Nitrog.'}
As you can see from above, method update()
added Nitrog.
to the dictionary. Now if the key existed, it will update the value:
chemical_elements.update({'N':'Nitrogen'})
chemical_elements
{'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H': 'Hydrogen',
'K': 'Potassium',
'N': 'Nitrogen'}
This method is particularly useful when you want to update values of multiple keys at the same time. While doing that, you can also add new values:
chemical_elements.update({
'K': 'Potasium',
'P': 'Phosphorus'
})
chemical_elements
{'O': 'Oxygen',
'C': 'Carbon',
'Na': 'Sodium',
'H': 'Hydrogen',
'K': 'Potasium',
'N': 'Nitrogen',
'P': 'Phosphorus'}
6.2.1.8. Dictionary comprehension#
Dictionary comprehensions, just like list comprehensions, are a quick way to create a dictionary. The syntax is:
new_dictionary = {key: value for key, value in old_dictionary.items()}
Notice that the syntax above will create a dictionary that is the same as the one we are using (old_dictionary
). You can use lists, or any other iterable to create the dictionary using the comprehension.
One use case of dictionary comprehension is when we want to reverse a dictionary (i.e., swap the values and keys) given that the values are also unique:
chemical_elements_reverse = {
name: symbol for symbol, name in chemical_elements.items()
}
chemical_elements_reverse
{'Oxygen': 'O',
'Carbon': 'C',
'Sodium': 'Na',
'Hydrogen': 'H',
'Potasium': 'K',
'Nitrogen': 'N',
'Phosphorus': 'P'}
As you can see, we used dictionary comprehension to reverse the dictionary by making keys values and values keys.
Note
Note that if values are not unique, then the dictionary will simply update the value of the duplicate key.
Let us look at another example. We are going to use a list of tuples to create a dictionary:
months = [
('January', 31), ('February', 28),
('March',31), ('April', 30), ('May', 31)
]
months_dict = {month: days for month, days in months}
months_dict
{'January': 31, 'February': 28, 'March': 31, 'April': 30, 'May': 31}
As you can see, the dictionary was created by unpacking each of the tuples in the months
. Note that the keys and values can be modified as well.
6.2.1.9. Comparing dictionaries#
Unlike lists, two dictionaries are equal if they have the same elements, no matter their order. For example:
chemical_elements_1 = {
'O': 'Oxygen', 'C': 'Carbon',
'Na': 'Sodium', 'H': 'Hydrogen',
'N': 'Nitrogen'
}
chemical_elements_2 = {
'O': 'Oxygen', 'C': 'Carbon',
'N': 'Nitrogen', 'Na': 'Sodium',
'H': 'Hydrogen'
}
chemical_elements_1 == chemical_elements_2
True
6.2.1.10. Copying dictionaries#
The idea of shallow- and deep-copy from Chapter 5 appears here again. If we do: dict1 = dict2
, then we are creating a shallow copy because we are simply adding a second pointer to the memory space of the first dictionary. If we make any changes to dict1
or dict2
, then they will appear in the other one as well.
chemical_elements_1 = {
'O': 'Oxygen', 'C': 'Carbon',
'Na': 'Sodium', 'H': 'Hydrogen',
'N': 'Nitrogen'
}
chemical_elements_2 = chemical_elements_1
print('chemical_elements_1', chemical_elements_1)
print('chemical_elements_2', chemical_elements_2)
chemical_elements_1 {'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen', 'N': 'Nitrogen'}
chemical_elements_2 {'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen', 'N': 'Nitrogen'}
Now let us change one of the elements in chemical_elements_1
and chemical_elements_2
and see what happens:
chemical_elements_1['Ag'] = 'Silver'
chemical_elements_2['Au'] = 'Gold'
print('chemical_elements_1', chemical_elements_1)
print('chemical_elements_2', chemical_elements_2)
chemical_elements_1 {'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen', 'N': 'Nitrogen', 'Ag': 'Silver', 'Au': 'Gold'}
chemical_elements_2 {'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen', 'N': 'Nitrogen', 'Ag': 'Silver', 'Au': 'Gold'}
As you can see, both elements are added in both dictionaries, although each addition was added to only one of them.
To circumvent this, we can use the dictionary1.copy()
method or the the dict(dictionary1)
function. The first method will copy the original dictionary’s data into another memory location and will add the pointer to that location. The second method will create a new dictionary from scratch by using the values of the original dictionary. In other words, dict()
is called a constructor for dictionaries. It is a function that can build/construct dictionaries.
chemical_elements_2 = chemical_elements_1.copy()
chemical_elements_2.popitem() # remove last element
print('chemical_elements_1', chemical_elements_1)
print('chemical_elements_2', chemical_elements_2)
chemical_elements_1 {'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen', 'N': 'Nitrogen', 'Ag': 'Silver', 'Au': 'Gold'}
chemical_elements_2 {'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen', 'N': 'Nitrogen', 'Ag': 'Silver'}
As you can see, the Au: Gold
key-value pair is removed only from the second dictionary. The original one is not affected.
chemical_elements_2 = dict(chemical_elements_1)
chemical_elements_2.popitem() # remove last element
print('chemical_elements_1', chemical_elements_1)
print('chemical_elements_2', chemical_elements_2)
chemical_elements_1 {'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen', 'N': 'Nitrogen', 'Ag': 'Silver', 'Au': 'Gold'}
chemical_elements_2 {'O': 'Oxygen', 'C': 'Carbon', 'Na': 'Sodium', 'H': 'Hydrogen', 'N': 'Nitrogen', 'Ag': 'Silver'}
The same happens above, but this time using dict()
.