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

man["city"] = "Baghdad"

print(man)
{'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

man = {}

print(man)
{}
man["city"] = "Baghdad"
man["name"] = "Baqir -al- Sadr"
man["born"] = 1935
man["citizenship"] = "Iraq"
man["died"] = 1979

print(man)
{'city': 'Baghdad', 'name': 'Baqir -al- Sadr', 'born': 1935, 'citizenship': 'Iraq', 'died': 1979}

The values to different keys in a dictionary can be same.

man["birth_place"] = "Baghdad"
man["death_place"] = "Baghdad"

print(man)
{'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.

man["died"] = 1980
print(man)
{'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.

colonies = {"british": ["India", "Australia"],
            "french": "Libya",
            "polish": 0,
            "german": 3.5,  # most of their colonies are split and joined into new countires.
            "cuba": None}

print(colonies)
{'british': ['India', 'Australia'], 'french': 'Libya', 'polish': 0, 'german': 3.5, 'cuba': None}

But the keys of a dictionary must be immutable.

persons = {1: "Adam",
           "Two": "Eva"}

print(persons)
{1: 'Adam', 'Two': 'Eva'}
# uncomment following line
# persons[[0, 1]] = ["Adam", "Eva"]  # TypeError

# Making a real practical dictionary

ur_per = {"admi": "mard", "aurat": "zan", "bacha": "tefl", "paighambar": "paighambar"}
per_ar = {"mard": "rojol", "zan": "nissa", "tefl": "tefl", "paighambar": "paighambar"}

print("The Arabic word for aurat is: " + per_ar[ur_per["aurat"]])
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.

books = {"AlSadr": ["Our Philosophy", "Our Economy"],
         "Mutahri": ["Divine Justice", "Man and Destiny"]
         }
keys = books.keys()

print(keys)
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

values = books.values()

values = list(values)

print(values)

print(type(values))
[['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.

capitals = ["Quetta", "Karachi", "Peshawar", "Lahore"]
provinces = ["Balochistan", "Sindh", "KPK", "Punjab"]

We can make use of zip function to convert these two lists into a generator. More about generators and zip will come later.

<zip object at 0x7ffad3e83480>
[('Balochistan', 'Quetta'), ('Sindh', 'Karachi'), ('KPK', 'Peshawar'), ('Punjab', 'Lahore')]

Now we can convert provinces_capitals into dictionary by making use of dict function.

{'Balochistan': 'Quetta', 'Sindh': 'Karachi', 'KPK': 'Peshawar', 'Punjab': 'Lahore'}

We can do all this in one step as follows

capitals = ["Quetta", "Karachi", "Peshawar", "Lahore"]
provinces = ["Balochistan", "Sindh", "KPK", "Punjab", "FATA"]

dict(zip(provinces, capitals))
{'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#

man = {"name": "Baqir -al- Sadr",
       "born": 1935,
       "citizenship": "Iraq",
       "died": 1979}

len(man)
4

in#

print("died" in man)
True
del man["died"]
print("died" not in man)
True

Repeating the above code will result in error.

We can also combine in with not

print("city" not in man)
True

pop#

man = {"name": "Baqir -al- Sadr",
       "born": 1935,
       "citizenship": "Iraq",
       "died": 1979}

man.pop("died")
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#

man = {"name": "Baqir -al- Sadr",
       "born": 1935,
       "citizenship": "Iraq",
       "died": 1979}

man.popitem()
('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.

man = {"name": "Baqir -al- Sadr",
       "born": 1935,
       "citizenship": "Iraq",
       "died": 1979}

print(man.get("city"))
None
man.get("city", "Baghdad")
'Baghdad'

copy#

Simple object assignment with = makes a shallow copy.

man1 = {"name": "Baqir -al- Sadr",
        "born": 1935,
        "citizenship": "Iraq",
        "died": 1979}

man2 = man1
man2["name"] = "Mutahri"

print(man1)
{'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.

man1 = {"name": "Baqir -al- Sadr",
        "born": 1935,
        "citizenship": "Iraq",
        "died": 1979}

man2 = man1.copy()

man2["name"] = "Mutahri"
print(man1)
print(man2)
{'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.

men1 = {1: {"name": "Baqir -al- Sadr", "born": 1935, "citizenship": "Iraq", "died": 1980},
        2: {"name": "Mutahri", "born": 1919, "citizenship": "Iran", "died": 1979}}

men2 = men1.copy()

men2[2]["name"] = "Murtaza Mutahri"

print(men1)
print(men2)
{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.

books1 = {"AlSadr": ["Our Philosophy", "Our Economy"],
          "Mutahri": ["Divine Justice", "Man and Destiny"]}

books2 = books1.copy()

books2["Mutahri"][1] = "The goal of life"
print(books1)
print(books2)
{'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

import copy

men1 = {1: {"name": "Baqir -al- Sadr", "born": 1935, "citizenship": "Iraq", "died": 1980},
        2: {"name": "Mutahri", "born": 1919, "citizenship": "Iran", "died": 1979}}

men2 = copy.copy(men1)

men2[2]["name"] = "Murtaza Mutahri"

print(men1)
print(men2)
{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

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': '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.

from copy import deepcopy

men1 = {1: {"iraq": {'person1': {'name': 'sadr'}, 'person2': {'name': 'hakim'}},
            "iran": {'person1': {'name': 'mutahri'}, 'person2': {'name': 'shariati'}}}}

men2 = deepcopy(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'}}}}

update#

This method updates an existing dictionary. %%

books = {"AlSadr": ["Our Philosophy", "Our Economy"],
         "Mutahri": ["Divine Justice", "Man and Destiny"]
         }

new_books = {"Legenhausen": ["Religious pluralism", "Hegel's ethics"]}

The method does not return anything. Only the original dictionary is changed.

books.update(new_books)

print(books)
{'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)

old_books = {"AlSadr": ["Our Philosophy", "Our Economy"],
             "Mutahri": ["Divine Justice", "Man and Destiny"]
             }

new_books = {"Legenhausen": ["Religious pluralism", "Hegel's ethics"]}

books = {**old_books, **new_books}

print(books)
{'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.

print(old_books)

print(new_books)
{'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.

books = {**old_books, "Iqbal": "reconstruction", **new_books}

print(books)
{'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

books = old_books.copy()
books.update(new_books)
print(books)
{'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.

old_books = {"AlSadr": ["Our Philosophy", "Our Economy"],
             "Mutahri": ["Divine Justice", "Man and Destiny"]}

new_books = {"Legenhausen": ["Religious pluralism", "Hegel's ethics"]}

books = dict(list(old_books.items()) + list(new_books.items()))
print(books)
{'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)

Gallery generated by Sphinx-Gallery