1.6 dictionary
Contents
Note
Click here to download the full example code or to run this example in your browser via Binder
1.6 dictionary#
This lesson describes a special data structure of python called dictionary
.
Intro#
Dictionaries are data containers that store the data as key, value pairs. Each value in a dictionary is associated with a key, and threfore every key must have a value associated with it. Therefore, dictionaries are also sometimes known as associative arrays.
We can define a dictionary using curly brackets “{}”. Each key and value pair must be separated by a comma “,” while a colon “:” is used to separate a key from its vlaue.
man = {"name": "Baqir -al- Sadr",
"born": 1935,
"citizenship": "Iraq",
"died": 1979,
"0": 0
}
We can verify that whether a python object is dictionary or not by checking its type
.
A variable which is a dictionary has a dict
type.
print(type(man))
<class 'dict'>
We can access data from a dictionary by making
use of slice operator []
.
print(man["name"])
Baqir -al- Sadr
Inside the square bracket, we can write any key
which is present
in the dicionary and we will get the value associated with it.
print(man["citizenship"])
Iraq
If we try to a access value whose corresponding key does not exist in the dictionary,
we will get KeyError
.
# uncomment following line
# man["city"] # -> KeyError
The error suggests that the dictionary man
does not have a key named city.
The key must be the same object as when it was defined. We can not use indexing for keys such as
# uncomment following line
# man[0] # KeyError
The man
key does have a key with the name ‘0’ but this is string type
and we provide 0 as integer and threfore we got KeyError.
We can add a new key, value pair in an existing dictionary as following
{'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1979, '0': 0, 'city': 'Baghdad'}
Thus we can start with an empty dictionary and populate it later on
{}
{'city': 'Baghdad', 'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1979}
The values to different keys in a dictionary can be same.
{'city': 'Baghdad', 'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1979, 'birth_place': 'Baghdad', 'death_place': 'Baghdad'}
But we can not have a dictionary with two or more same keys. If we add a new key with same name, the previous key, value will be replaced.
{'city': 'Baghdad', 'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1980, 'birth_place': 'Baghdad', 'death_place': 'Baghdad'}
type of keys and values#
The values in a dictionary can be of any type.
{'british': ['India', 'Australia'], 'french': 'Libya', 'polish': 0, 'german': 3.5, 'cuba': None}
But the keys of a dictionary must be immutable.
{1: 'Adam', 'Two': 'Eva'}
# uncomment following line
# persons[[0, 1]] = ["Adam", "Eva"] # TypeError
# Making a real practical dictionary
The Arabic word for aurat is: nissa
keys
and values
methods#
We can get all the keys of a dictionary using .keys()
method on dictionary.
The dot “.” here signifies that the keys()
function comes from dictionary.
This means any variable which is a dictionary, will have .keys() function in it.
dict_keys(['AlSadr', 'Mutahri'])
Algthouh the printing keys look like list
but in reality their time is not list
.
print(type(keys))
<class 'dict_keys'>
We can convert keys of a dictionary into list
type as follows
keys_as_list = list(keys)
print(type(keys_as_list))
<class 'list'>
print(keys_as_list)
['AlSadr', 'Mutahri']
Similarly we can convert values of a dictionary into list
type as follows
[['Our Philosophy', 'Our Economy'], ['Divine Justice', 'Man and Destiny']]
<class 'list'>
The function items()
when applied on a dictionay, returns both keys and values.
items = books.items()
print(items)
dict_items([('AlSadr', ['Our Philosophy', 'Our Economy']), ('Mutahri', ['Divine Justice', 'Man and Destiny'])])
print(type(items))
<class 'dict_items'>
books_as_list = list(books.items())
print(books_as_list)
[('AlSadr', ['Our Philosophy', 'Our Economy']), ('Mutahri', ['Divine Justice', 'Man and Destiny'])]
print(type(books_as_list[0]))
<class 'tuple'>
print(books_as_list[0])
('AlSadr', ['Our Philosophy', 'Our Economy'])
dictionaries from lists#
We can convert a list into dictionary if each member in the list is a tuple.
We can make use of zip
function to convert these two lists into
a generator. More about generators and zip will come later.
provinces_capitals_iterator = zip(provinces, capitals)
print(provinces_capitals_iterator)
<zip object at 0x7ffad3e83480>
provinces_capitals = list(provinces_capitals_iterator)
print(provinces_capitals)
[('Balochistan', 'Quetta'), ('Sindh', 'Karachi'), ('KPK', 'Peshawar'), ('Punjab', 'Lahore')]
Now we can convert provinces_capitals into dictionary by making use of dict
function.
provinces_capitals_dict = dict(provinces_capitals)
print(provinces_capitals_dict)
{'Balochistan': 'Quetta', 'Sindh': 'Karachi', 'KPK': 'Peshawar', 'Punjab': 'Lahore'}
We can do all this in one step as follows
{'Balochistan': 'Quetta', 'Sindh': 'Karachi', 'KPK': 'Peshawar', 'Punjab': 'Lahore'}
provinces_capitals_dict = dict(
list(zip(["Balochistan", "Sindh", "KPK", "Punjab"], ["Quetta", "Karachi", "Peshawar", "Lahore"])))
print(provinces_capitals_dict)
{'Balochistan': 'Quetta', 'Sindh': 'Karachi', 'KPK': 'Peshawar', 'Punjab': 'Lahore'}
Operations on dictionaries#
Following examples show, how to apply different operations with dictionaries.
len
#
4
in
#
print("died" in man)
True
True
Repeating the above code will result in error.
We can also combine in
with not
print("city" not in man)
True
pop
#
1979
print(man)
{'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq'}
If we try to remove a non-existing key using pop
, it will throw KeyError.
# uncomment following line
# man.pop("died") # KeyError
However, we can avoid this error by supplying the default value that needs to be returned.
man.pop("dob", 19350101)
19350101
Therefore, we can use this method to avoid the error of removing the key from a dictionary if the key is not present in dictionary.
man.pop("died", None)
print(man)
{'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq'}
poopitem
#
('died', 1979)
print(man)
{'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq'}
man.popitem()
('citizenship', 'Iraq')
print(man)
{'name': 'Baqir -al- Sadr', 'born': 1935}
get
#
This method can also be used for accessing the values in dictionary. It returns None if the key is not present and we can set the default value for a key if the value is not already present.
None
man.get("city", "Baghdad")
'Baghdad'
copy
#
Simple object assignment with =
makes a shallow copy.
{'name': 'Mutahri', 'born': 1935, 'citizenship': 'Iraq', 'died': 1979}
print(man2)
{'name': 'Mutahri', 'born': 1935, 'citizenship': 'Iraq', 'died': 1979}
so even though we changed the name
of man2
, but name
of man1
is also changed.
{'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1979}
{'name': 'Mutahri', 'born': 1935, 'citizenship': 'Iraq', 'died': 1979}
Now we don’t see name
of man1
dictionary from getting changed.
This is because we made a copy of man1
and set this copy to man2
.
After that we changed man2
key.
{1: {'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1980}, 2: {'name': 'Murtaza Mutahri', 'born': 1919, 'citizenship': 'Iran', 'died': 1979}}
{1: {'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1980}, 2: {'name': 'Murtaza Mutahri', 'born': 1919, 'citizenship': 'Iran', 'died': 1979}}
Even though we made a copy of men1
dictionary using copy
method
but its contents are still changed when we change men2
.
This is because copy
method still makes a shallow copy for dictionary
inside the dictionary.
Same is true for list in the dictionaries.
{'AlSadr': ['Our Philosophy', 'Our Economy'], 'Mutahri': ['Divine Justice', 'The goal of life']}
{'AlSadr': ['Our Philosophy', 'Our Economy'], 'Mutahri': ['Divine Justice', 'The goal of life']}
How to copy a dictionary which may contain several dictionaries?
we can make use of copy
by iterating over dictionary
{1: {'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1980}, 2: {'name': 'Murtaza Mutahri', 'born': 1919, 'citizenship': 'Iran', 'died': 1979}}
{1: {'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1980}, 2: {'name': 'Murtaza Mutahri', 'born': 1919, 'citizenship': 'Iran', 'died': 1979}}
if we iterate through each key, value pair of dictionary and copy it individually, we can avoid this shallow copying
def copy_dict(d: dict) -> dict:
"""makes deepcopy of a dictionary without cloning it"""
assert isinstance(d, dict)
new_dict = {}
for k, v in d.items():
new_dict[k] = copy.copy(v)
return new_dict
men1 = {1: {"name": "Baqir -al- Sadr", "born": 1935, "citizenship": "Iraq", "died": 1980},
2: {"name": "Mutahri", "born": 1919, "citizenship": "Iran", "died": 1979}}
men2 = copy_dict(men1)
men2[2]["name"] = "Murtaza Mutahri"
print(men1)
print(men2)
{1: {'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1980}, 2: {'name': 'Mutahri', 'born': 1919, 'citizenship': 'Iran', 'died': 1979}}
{1: {'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1980}, 2: {'name': 'Murtaza Mutahri', 'born': 1919, 'citizenship': 'Iran', 'died': 1979}}
but what if dictionary inside the dictionary further contains dictionaries
{1: {'iraq': {'person1': {'name': 'baqir al sadr'}, 'person2': {'name': 'hakim'}}, 'iran': {'person1': {'name': 'mutahri'}, 'person2': {'name': 'shariati'}}}}
{1: {'iraq': {'person1': {'name': 'baqir al sadr'}, 'person2': {'name': 'hakim'}}, 'iran': {'person1': {'name': 'mutahri'}, 'person2': {'name': 'shariati'}}}}
although we changed name
of person1
in men2
but it is also changed in men1
.
we can achieve this by calling the parent function again every
time the value is a dictionary i.e. calling copy_dict
function inside copy_dict
function.
def copy_dict(d: dict) -> dict:
"""makes deepcopy of a dictionary without cloning it"""
assert isinstance(d, dict)
new_dict = {}
for k, v in d.items():
if isinstance(v, dict):
new_dict[k] = copy_dict(v)
else:
new_dict[k] = copy.copy(v)
return new_dict
men1 = {1: {"iraq": {'person1': {'name': 'sadr'}, 'person2': {'name': 'hakim'}},
"iran": {'person1': {'name': 'mutahri'}, 'person2': {'name': 'shariati'}}}}
men2 = copy_dict(men1)
men2[1]["iraq"]['person1']['name'] = "baqir al sadr"
print(men1)
print(men2)
{1: {'iraq': {'person1': {'name': 'sadr'}, 'person2': {'name': 'hakim'}}, 'iran': {'person1': {'name': 'mutahri'}, 'person2': {'name': 'shariati'}}}}
{1: {'iraq': {'person1': {'name': 'baqir al sadr'}, 'person2': {'name': 'hakim'}}, 'iran': {'person1': {'name': 'mutahri'}, 'person2': {'name': 'shariati'}}}}
However, there is simpler solution to this problem. Instead of writting a function
like copy_dict, which copies each object from dictionary one by one, we can simply
use deepcopy
function from copy
module.
{1: {'iraq': {'person1': {'name': 'sadr'}, 'person2': {'name': 'hakim'}}, 'iran': {'person1': {'name': 'mutahri'}, 'person2': {'name': 'shariati'}}}}
{1: {'iraq': {'person1': {'name': 'baqir al sadr'}, 'person2': {'name': 'hakim'}}, 'iran': {'person1': {'name': 'mutahri'}, 'person2': {'name': 'shariati'}}}}
update
#
This method updates an existing dictionary. %%
The method does not return anything. Only the original dictionary is changed.
{'AlSadr': ['Our Philosophy', 'Our Economy'], 'Mutahri': ['Divine Justice', 'Man and Destiny'], 'Legenhausen': ['Religious pluralism', "Hegel's ethics"]}
Merging dictionaries#
The update merges one dictionary into other. If we want to keep both dictionaries intact and create a new one by merging them together, we can do this as following (starting from python 3.5)
{'AlSadr': ['Our Philosophy', 'Our Economy'], 'Mutahri': ['Divine Justice', 'Man and Destiny'], 'Legenhausen': ['Religious pluralism', "Hegel's ethics"]}
We can verify that old_books and new_books dictionaries are intact.
{'AlSadr': ['Our Philosophy', 'Our Economy'], 'Mutahri': ['Divine Justice', 'Man and Destiny']}
{'Legenhausen': ['Religious pluralism', "Hegel's ethics"]}
We can even provide a new key value pair.
{'AlSadr': ['Our Philosophy', 'Our Economy'], 'Mutahri': ['Divine Justice', 'Man and Destiny'], 'Iqbal': 'reconstruction', 'Legenhausen': ['Religious pluralism', "Hegel's ethics"]}
The double asterisk **
, in fact, just unpacks the dictionary into key value
pairs and then we construct a new dictionary by putting the unpacked key value
pairs inside curly brackets “{}”.
print({'x': 1, **{'y': 2}})
{'x': 1, 'y': 2}
For backup compatability, we better use the update
method that can run on versions
before 3.5 as follows
{'AlSadr': ['Our Philosophy', 'Our Economy'], 'Mutahri': ['Divine Justice', 'Man and Destiny'], 'Legenhausen': ['Religious pluralism', "Hegel's ethics"]}
We can also merge two dictionaries with another method.
{'AlSadr': ['Our Philosophy', 'Our Economy'], 'Mutahri': ['Divine Justice', 'Man and Destiny'], 'Legenhausen': ['Religious pluralism', "Hegel's ethics"]}
Total running time of the script: ( 0 minutes 0.023 seconds)