python-course.eu

12. Dictionaries

By Bernd Klein. Last modified: 11 Feb 2024.

Introduction

In the preceding chapters, we delved into the fundamentals of lists, , including subtle intricacies. Now, as we progress through this chapter of our online Python course, we turn our attention to dictionaries, exploring their syntax, operators, and methods. Lists and dictionaries are indispensable components of Python programming, making Python a powerful and extremely useful programming language.

Websters Dictionary

Let's begin with a straightforward example to illustrate the concept of a dictionary:

person = {
    'name': 'Anna Schmidt',
    'age': 28,
    'city': 'Berlin',
    'email': '[email protected]'
}

In Python, a dictionary is a data structure that stores key-value pairs. Let's take a closer look at our example dictionary to explain the essential details:

Dictionaries are useful for organizing data in a way that allows quick and easy access based on specific keys.

We can also consider a dictionary like a digital information card about a person named Anna Schmidt from Berlin, Germany and keeps track of important details in a structured way.

In this digital card:

In the code snippet below, we demonstrate how to retrieve specific pieces of information from our dictionary, such as the 'name' or 'age', from the dictionary:

# Retrieving values
name = person['name']
age = person['age']

print("Name:", name)
print("Age:", age)

OUTPUT:

Name: Anna Schmidt
Age: 28

Now, we'll illustrate how dictionaries can be modified because they are mutable. Let's consider a scenario where Anna relocates from Berlin to Freiburg, renowned as one of Germany's most picturesque cities, known for its favorable weather compared to other regions in the country.

# Anna moves to Freiburg
person['city'] = 'Freiburg'

# Updated information
print(person)

OUTPUT:

{'name': 'Anna Schmidt', 'age': 28, 'city': 'Freiburg', 'email': '[email protected]'}

Like lists, they can be easily changed, can be shrunk and grown ad libitum at run time. They shrink and grow without the necessity of making copies. Dictionaries can be contained in lists and vice versa.

Let's add Anna's gender and her favorite hobbies, which include reading, music, and programming (as she's a beginner in Python), to the existing dictionary:

person['sex'] = 'female'
person['favorite_hobbies'] = ['reading', 'music', 'programming']

person

OUTPUT:

{'name': 'Anna Schmidt',
 'age': 28,
 'city': 'Freiburg',
 'email': '[email protected]',
 'favorite_hobbies': ['reading', 'music', 'programming'],
 'sex': 'female'}

Now, we remove again the gender information from the dictionary:

# Deleting gender information
del person['sex']
print(person)

OUTPUT:

{'name': 'Anna Schmidt', 'age': 28, 'city': 'Freiburg', 'email': '[email protected]', 'favorite_hobbies': ['reading', 'music', 'programming']}

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Enrol here

Associative Arrays

More theoretically, we can say that dictionaries are the Python implementation of an abstract data type, known in computer science as an associative array. Associative arrays consist - like dictionaries of (key, value) pairs, such that each possible key appears at most once in the collection. Any key of the dictionary is associated (or mapped) to a value. The values of a dictionary can be any type of Python data. So, dictionaries are unordered key-value-pairs. Dictionaries are implemented as hash tables, and that is the reason why they are known as "Hashes" in the programming language Perl.

Dictionaries don't support the sequence operation of the sequence data types like strings, tuples and lists. Dictionaries belong to the built-in mapping type, but so far, they are the sole representative of this kind!

At the end of this chapter, we will demonstrate how a dictionary can be turned into one list, containing (key,value)-tuples or two lists, i.e. one with the keys and one with the values. This transformation can be done reversely as well.

Dictionary and Lists

But what's the difference between lists and dictionaries?

A list is a sequence of objects where the order matters. You can access list elements directly by their position in this order. Similarly, dictionaries are also ordered, but unlike lists, you can't access elements by their position. We can also say that contrary to lists that the order of a dictionary is not important. The order of items in a dictionary is determined by the sequence in which they were added. If we print a dictionary, we can see this ordering.

Let's demonstrate this with the following dictionary:

city_population = {
    'Berlin': (3769495, 'Germany'),
    'Paris': (2161000, 'France'),
    'Vienna': (1921641, 'Austria'),
    'Hamburg': (1841179, 'Germany'),
    'Amsterdam': (872680, 'Netherlands'),
    'Rotterdam': (651446, 'Netherlands'),
    'Stuttgart': (634830, 'Germany'),
    'Zurich': (415215, 'Switzerland'),
    'Graz': (290571, 'Austria'),
    'Strasbourg': (280966, 'France'),
    'Freiburg': (230241, 'Germany'),
    'Geneva': (201818, 'Switzerland'),
    'Basel': (178128, 'Switzerland'),
    'Salzburg': (155031, 'Austria'),
    'Luxembourg City': (124528, 'Luxembourg'),
    'Metz': (117619, 'France')
}

We will add now the German cities "München" (Munich) and "Köln' (Cologne) to our dictionary:

city_population['Munich'] = (1471508, 'Germany')
city_population['Cologne'] = (1085664, 'Germany')
print(city_population)

OUTPUT:

{'Berlin': (3769495, 'Germany'), 'Paris': (2161000, 'France'), 'Vienna': (1921641, 'Austria'), 'Hamburg': (1841179, 'Germany'), 'Amsterdam': (872680, 'Netherlands'), 'Rotterdam': (651446, 'Netherlands'), 'Stuttgart': (634830, 'Germany'), 'Zurich': (415215, 'Switzerland'), 'Graz': (290571, 'Austria'), 'Strasbourg': (280966, 'France'), 'Freiburg': (230241, 'Germany'), 'Geneva': (201818, 'Switzerland'), 'Basel': (178128, 'Switzerland'), 'Salzburg': (155031, 'Austria'), 'Luxembourg City': (124528, 'Luxembourg'), 'Metz': (117619, 'France'), 'Munich': (1471508, 'Germany'), 'Cologne': (1085664, 'Germany')}

We can see that the cities "München" (Munich) and "Köln' (Cologne) had been added to the end of the dictionary as expected.

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Upcoming online Courses

Enrol here

Convert Dictionaries to Lists

If you have worked for a while with Python, nearly inevitably the moment will come, when you want or have to convert lists into dictionaries or vice versa. It wouldn't be too hard to write a function doing this. But Python wouldn't be Python, if it didn't provide such functionalities. We can convert the previously defined dictionary into a list with the following expression:

city_population_list = list(city_population.items())
city_population_list

OUTPUT:

[('Berlin', (3769495, 'Germany')),
 ('Paris', (2161000, 'France')),
 ('Vienna', (1921641, 'Austria')),
 ('Hamburg', (1841179, 'Germany')),
 ('Amsterdam', (872680, 'Netherlands')),
 ('Rotterdam', (651446, 'Netherlands')),
 ('Stuttgart', (634830, 'Germany')),
 ('Zurich', (415215, 'Switzerland')),
 ('Graz', (290571, 'Austria')),
 ('Strasbourg', (280966, 'France')),
 ('Freiburg', (230241, 'Germany')),
 ('Geneva', (201818, 'Switzerland')),
 ('Basel', (178128, 'Switzerland')),
 ('Salzburg', (155031, 'Austria')),
 ('Luxembourg City', (124528, 'Luxembourg')),
 ('Metz', (117619, 'France')),
 ('Munich', (1471508, 'Germany')),
 ('Cologne', (1085664, 'Germany'))]

This list holds the identical information as the dictionary city_population. However, retrieving this data differs significantly. What if you want to get the population of Amsterdam? It's extremely easy with the dictionary:

city_population['Amsterdam'][0]

OUTPUT:

872680

It's a lot more complicated in the list representation. We're limited to accessing indices rather than city names. Consequently, we must iterate over the list and inspect all the tuples to determine if the first entry corresponds to the city of Amsterdam.

# Given list of tuples
city_population_list = [
    ('Berlin', (3769495, 'Germany')),
    ('Paris', (2161000, 'France')),
    ('Vienna', (1921641, 'Austria')),
    ('Hamburg', (1841179, 'Germany')),
    ('Amsterdam', (872680, 'Netherlands')),
    ('Rotterdam', (651446, 'Netherlands')),
    ('Stuttgart', (634830, 'Germany')),
    ('Zurich', (415215, 'Switzerland')),
    ('Graz', (290571, 'Austria')),
    ('Strasbourg', (280966, 'France')),
    ('Freiburg', (230241, 'Germany')),
    ('Geneva', (201818, 'Switzerland')),
    ('Basel', (178128, 'Switzerland')),
    ('Salzburg', (155031, 'Austria')),
    ('Luxembourg City', (124528, 'Luxembourg')),
    ('Metz', (117619, 'France')),
    ('Munich', (1471508, 'Germany')),
    ('Cologne', (1085664, 'Germany'))
]

# Prompting the user for a city name
city_name = input("Enter a city name: ")

# Finding the population of the city
population = None
for city, (pop, _) in city_population_list:
    if city_name.lower() == city.lower():
        population = pop
        break

# Displaying the population if found, or a message if not found
if population is not None:
    print(f"The population of {city_name} is {population}")
else:
    print(f"Population data for {city_name} is not available.")

OUTPUT:

The population of Amsterdam is 872680

In this aspect, the dictionary method outperforms the list approach by enabling swifter data access. In a dictionary, you can directly retrieve the population of a city using its name as the key, which is much quicker compared to searching through a list to find the population. Think of it like looking up a word in a dictionary versus flipping through the pages of a book to find it. The dictionary provides instant access, while the list requires more time to search through.

The efficiency of this algorithm depends on the size of the city_population_list. Since it iterates through the entire list linearly to find the population of a given city, the time complexity is O(n), where n is the number of cities in the list.

For a small number of cities, this approach is acceptable. However, as the number of cities grows larger, the linear search becomes less efficient, especially if the city being searched for is towards the end of the list or if the list contains a large number of cities.

In scenarios where performance is critical or when dealing with a large dataset, a more efficient approach would be to use a dictionary instead of a list, where the keys are the city names. This would allow constant-time (O(1)) lookups, providing faster access to population data.

items()

If we apply the method items() to a dictionary, we don't get a list back, as it used to be the case in Python 2, but a so-called items view. The items view can be turned into a list by applying the list function. We have no information loss by turning a dictionary into an item view or an items list, i.e. it is possible to recreate the original dictionary from the view created by items().

Accessing dictionary items via a method like items() provides a more efficient approach compared to creating lists directly due to several reasons:

1) Lazy Evaluation: The items() method doesn't generate the entire list of key-value pairs upfront. Instead, it returns a view object that generates items on-the-fly as needed. This lazy evaluation saves memory and processing time, especially when dealing with large dictionaries where creating a complete list of items upfront may be resource-intensive.

2) Memory Efficiency: Generating a list of items directly requires storing all key-value pairs in memory simultaneously, which can be inefficient for large dictionaries. In contrast, using items() provides a memory-efficient solution as it generates items dynamically without storing the entire list in memory at once.

3) Time Complexity: While both approaches have similar time complexity for iterating over all items (O(n), where n is the number of items in the dictionary), using items() may provide better performance in certain scenarios. For example, if only a subset of items is needed, items() allows for efficient iteration over only the required items without iterating over the entire dictionary.

Overall, using methods like items() for accessing dictionary items provides a more efficient and memory-friendly approach, especially for large dictionaries or scenarios where lazy evaluation and selective item retrieval are beneficial.

keys()

The keys() method returns a view object that displays a list of all keys in the dictionary. This allows you to iterate over or access the keys directly without needing to create a separate list of keys.

city_info_dict = {'Stuttgart': (207.35, 634830), 'Karlsruhe': (173.46, 313092), 'Mannheim': (144.96, 309370), 'Freiburg': (153.07, 230241), 'Heidelberg': (108.83, 160355), 'Heilbronn': (99.88, 125960)}

# Using keys() to access the keys of the dictionary
city_names = city_info_dict.keys()

print("City Names:")
for city in city_names:
    print(city)

While the keys() method is available in Python dictionaries, it's less commonly used because iterating over a dictionary directly implicitly iterates over its keys. Here's an example demonstrating this:

city_info_dict = {'Zurich': (87.88, 434008), 'Geneva': (15.93, 201818), 'Basel': (23.91, 178015), 'Lausanne': (41.38, 146372), 'Bern': (51.62, 133798), 'Lucerne': (37.04, 82106), 'St. Gallen': (39.85, 75834)}

# Iterating over the dictionary directly, which implicitly iterates over keys
print("City Names:")
for city in city_info_dict:
    print(city)

OUTPUT:

City Names:
Zurich
Geneva
Basel
Lausanne
Bern
Lucerne
St. Gallen

values()

We can also iterate directly over the values of a dictionary:

# Iterating over the values of the dictionary using values() method
print("City Data (Area, Population):")
for area, population in city_info_dict.values():
    print("Area:", area, "sq. km, Population:", population)

OUTPUT:

City Data (Area, Population):
Area: 87.88 sq. km, Population: 434008
Area: 15.93 sq. km, Population: 201818
Area: 23.91 sq. km, Population: 178015
Area: 41.38 sq. km, Population: 146372
Area: 51.62 sq. km, Population: 133798
Area: 37.04 sq. km, Population: 82106
Area: 39.85 sq. km, Population: 75834

Turn Lists into Dictionaries

Zipper on a Ball

Now we will turn our attention to the art of cooking, but don't be afraid, this remains a python course and not a cooking course. We want to show you, how to turn lists into dictionaries, if these lists satisfy certain conditions. We have two lists, one containing the names of the largest German cities and the other one the corresponding areas:

cities = ['Berlin', 'Hamburg', 'Munich', 'Cologne', 'Frankfurt']
areas = [891.68, 755.3, 310.7, 405.02, 248.31]  # Areas are in square kilometers


# Using dict() and zip() to convert the lists into a dictionary
city_area_dict = dict(zip(cities, areas))

print(city_area_dict)

In this example, we have two lists: cities containing the names of the five largest cities in Germany, and areas containing their respective areas in square kilometers. We use the zip() function to combine these lists into a list of tuples, then we use the dict() function to convert this list of tuples into a dictionary, where each city is paired with its respective area.

To gain further insight into the functionality of the zip function, explore our tutorial available at 'zip'.

cities = ['Berlin', 'Hamburg', 'Munich', 'Cologne', 'Frankfurt']
areas = [891.68, 755.3, 310.7, 405.02, 248.31]  # Areas are in square kilometers
populations = [3769495, 1841179, 1471508, 1085664, 753056]

city_info_dict = {city: (area, population) for city, area, population in zip(cities, areas, populations)}

print(city_info_dict)

OUTPUT:

{'Berlin': (891.68, 3769495), 'Hamburg': (755.3, 1841179), 'Munich': (310.7, 1471508), 'Cologne': (405.02, 1085664), 'Frankfurt': (248.31, 753056)}

Danger Lurking

What is wrong with the following example:

fruits_dict = {
    'apple': 'Apfel',
    'banana': 'Banane',
    'orange': 'Orange',
    'grape': 'Traube',
    'strawberry': 'Erdbeere'
}

# Iterating over the items of the dictionary using items() method
print("Fruits and their German Translations:")
items = fruits_dict.items()
print(list(items))
for fruit, translation in items:
    print(f"{fruit.capitalize()} (English) -> {translation.capitalize()} (German)")

OUTPUT:

Fruits and their German Translations:
[('apple', 'Apfel'), ('banana', 'Banane'), ('orange', 'Orange'), ('grape', 'Traube'), ('strawberry', 'Erdbeere')]
Apple (English) -> Apfel (German)
Banana (English) -> Banane (German)
Orange (English) -> Orange (German)
Grape (English) -> Traube (German)
Strawberry (English) -> Erdbeere (German)

The issue with this example is that we are converting items to a list using list(items) before iterating over it. Once you convert items to a list, it becomes exhausted, meaning it doesn't contain any more elements for iteration.

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Enrol here

Incrementally Creating a Dictionary

So, it's possible to create a dictionary incrementally by starting with an empty dictionary. We haven't mentioned so far, how to define an empty one. It can be done by using an empty pair of brackets. The following defines an empty dictionary called city:

city_population = {}
city_population
city_population['Berlin'] = (3769495, 'Germany')
city_population['Stuttgart'] = (634830, 'Germany')
city_population

Closer Look at Keys and Values

Looking at our first examples with the cities and their population, you might have gotten the wrong impression that the values in the dictionaries have to be different. The values can be the same, as you can see in the following example. In honour to the patron saint of Python "Monty Python", we'll have now some special food dictionaries. What's Python without "bacon", "egg" and "spam"?

food = {"bacon": "yes", "egg": "yes", "spam": "no" }
food

OUTPUT:

{'bacon': 'yes', 'egg': 'yes', 'spam': 'no'}

Keys of a dictionary are unique. In casse a keys is defined multiple times, the value of the last "wins":

food = {"bacon" : "yes", "spam" : "yes", "egg" : "yes", "spam" : "no" }
food

OUTPUT:

{'bacon': 'yes', 'spam': 'no', 'egg': 'yes'}

Our next example is a simple English-German dictionary:

en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}
print(en_de)
print(en_de["red"])

OUTPUT:

{'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'}
rot

What about having another language dictionary, let's say German-French?

Now it's even possible to translate from English to French, even though we don't have an English-French-dictionary. de_fr[en_de["red"]] gives us the French word for "red", i.e. "rouge":

de_fr = {"rot": "rouge", "grün": "vert", "blau": "bleu", "gelb": "jaune"}
en_de = {"red": "rot", "green": "grün", "blue": "blau", "yellow": "gelb"}
print(en_de)

OUTPUT:

{'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'}
print(en_de["red"])

OUTPUT:

rot
de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}
print("The French word for red is: " + de_fr[en_de["red"]])

OUTPUT:

The French word for red is: rouge

We can use arbitrary types as values in a dictionary, but there is a restriction for the keys. Only immutable data types can be used as keys, i.e. no lists or dictionaries can be used: If you use a mutable data type as a key, you get an error message:

dic = {[1,2,3]: "abc"}

OUTPUT:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[39], line 1
----> 1 dic = {[1,2,3]: "abc"}

TypeError: unhashable type: 'list'

Tuple as keys are okay, as you can see in the following example:

dic = {(1, 2, 3): "abc", 3.1415: "abc"}
dic

OUTPUT:

{(1, 2, 3): 'abc', 3.1415: 'abc'}

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Enrol here

Nested Dictionaries

Nested dictionaries are dictionaries that contain other dictionaries as values. In Python, dictionaries can hold any data type as their values, including other dictionaries. This allows for the creation of hierarchical or nested data structures where each level of the dictionary can store its own set of key-value pairs.

Let's improve our examples with the natural language dictionaries a bit. We create now a nested dictionary, i.e. a dictionary of dictionaries:

en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}
de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}
de_tr = {"rot": "kırmızı", "grün": "yeşil", "blau": "mavi", "gelb": "jel"}
en_es = {"red" : "rojo", "green" : "verde", "blue" : "azul", "yellow":"amarillo"}

dictionaries = {"en_de" : en_de, "de_fr" : de_fr, "de_tr": de_tr, "en_es": en_es}
dictionaries

OUTPUT:

{'en_de': {'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'},
 'de_fr': {'rot': 'rouge', 'grün': 'vert', 'blau': 'bleu', 'gelb': 'jaune'},
 'de_tr': {'rot': 'kırmızı', 'grün': 'yeşil', 'blau': 'mavi', 'gelb': 'jel'},
 'en_es': {'red': 'rojo',
  'green': 'verde',
  'blue': 'azul',
  'yellow': 'amarillo'}}
cn_de = {"红": "rot", "绿" : "grün", "蓝" : "blau", "黄" : "gelb"}
de_ro = {'rot': 'roșu', 'gelb': 'galben', 'blau': 'albastru', 'grün': 'verde'}
de_hex = {"rot" : "#FF0000", "grün" : "#00FF00", "blau" : "0000FF", "gelb":"FFFF00"}
en_pl = {"red" : "czerwony", "green" : "zielony", 
         "blue" : "niebieski", "yellow" : "żółty"}
de_it = {"rot": "rosso", "gelb": "giallo", "blau": "blu", "grün": "verde"}

dictionaries["cn_de"] = cn_de
dictionaries["de_ro"] = de_ro
dictionaries["de_hex"] = de_hex
dictionaries["en_pl"] = en_pl
dictionaries["de_it"] = de_it
dictionaries

OUTPUT:

{'en_de': {'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'},
 'de_fr': {'rot': 'rouge', 'grün': 'vert', 'blau': 'bleu', 'gelb': 'jaune'},
 'de_tr': {'rot': 'kırmızı', 'grün': 'yeşil', 'blau': 'mavi', 'gelb': 'jel'},
 'en_es': {'red': 'rojo',
  'green': 'verde',
  'blue': 'azul',
  'yellow': 'amarillo'},
 'cn_de': {'红': 'rot', '绿': 'grün', '蓝': 'blau', '黄': 'gelb'},
 'de_ro': {'rot': 'roșu',
  'gelb': 'galben',
  'blau': 'albastru',
  'grün': 'verde'},
 'de_hex': {'rot': '#FF0000',
  'grün': '#00FF00',
  'blau': '0000FF',
  'gelb': 'FFFF00'},
 'en_pl': {'red': 'czerwony',
  'green': 'zielony',
  'blue': 'niebieski',
  'yellow': 'żółty'},
 'de_it': {'rot': 'rosso', 'gelb': 'giallo', 'blau': 'blu', 'grün': 'verde'}}
dictionaries["en_de"]     # English to German dictionary

OUTPUT:

{'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'}
dictionaries["de_fr"]     # German to French

OUTPUT:

{'rot': 'rouge', 'grün': 'vert', 'blau': 'bleu', 'gelb': 'jaune'}
print(dictionaries["de_fr"]["blau"])    # equivalent to de_fr['blau']

OUTPUT:

bleu
de_fr['blau']
lang_pair = input("Which dictionary, e.g. 'de_fr', 'en_de': ")
word_to_be_translated = input("Which colour: ")

d = dictionaries[lang_pair]
if word_to_be_translated in d:
    print(word_to_be_translated + " --> " + d[word_to_be_translated])
dictionaries['de_fr'][dictionaries['en_de']['red']]

Morsecode Example

The following dictionary contains a mapping from latin characters to morsecode.

morse = {
"A" : ".-", 
"B" : "-...", 
"C" : "-.-.", 
"D" : "-..", 
"E" : ".", 
"F" : "..-.", 
"G" : "--.", 
"H" : "....", 
"I" : "..", 
"J" : ".---", 
"K" : "-.-", 
"L" : ".-..", 
"M" : "--", 
"N" : "-.", 
"O" : "---", 
"P" : ".--.", 
"Q" : "--.-", 
"R" : ".-.", 
"S" : "...", 
"T" : "-", 
"U" : "..-", 
"V" : "...-", 
"W" : ".--", 
"X" : "-..-", 
"Y" : "-.--", 
"Z" : "--..", 
"0" : "-----", 
"1" : ".----", 
"2" : "..---", 
"3" : "...--", 
"4" : "....-", 
"5" : ".....", 
"6" : "-....", 
"7" : "--...", 
"8" : "---..", 
"9" : "----.", 
"." : ".-.-.-", 
"," : "--..--"
}

The numbers of characters contained in this dictionary can be determined by calling the len function:

len(morse)

OUTPUT:

38

The dictionary contains only upper case characters, so that "a" returns False, for example:

"a" in morse

OUTPUT:

False
"A" in morse

OUTPUT:

True
"a" not in morse

OUTPUT:

True
word = input("Your word: ")

for char in word.upper():
    print(char, morse[char])

OUTPUT:

P .--.
Y -.--
T -
H ....
O ---
N -.

Now, we will output a space character with three spaces in the morsecode encoding:

word = input("Your word: ")

morse_word = ""
for char in word.upper():
    if char == " ":
        morse_word += "   "
    else:
        if char not in morse:
            continue          # continue with next char, go back to for
        morse_word += morse[char] + " "

print(morse_word)

OUTPUT:

.--. -.-- - .... --- -.    .. ...    --. .-. . .- - 

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Enrol here

pop() and popitem()

pop

Lists can serve as stacks, with the pop() method used to extract an element from the stack. This functionality is straightforward for lists. However, considering dictionaries, does it make sense to have a pop() method? Unlike lists, dictionaries are not sequential data structures; they lack indexing. Consequently, the pop() method behaves differently with dictionaries.

In dictionaries, the pop() method is used to remove a specific key-value pair from the dictionary and return the corresponding value. Here's how it works:

1) You specify the key of the item you want to remove as an argument to the pop() method. 2) The pop() method then searches for this key in the dictionary. 3) If the key is found, the corresponding key-value pair is removed from the dictionary, and the value associated with the key is returned. 4) If the key is not found, a KeyError is raised.

Here's an example to illustrate:

capitals = {"Austria":"Vienna", "Germany":"Berlin", "Netherlands":"Amsterdam", "Switzerland":"Bern"}
capital = capitals.pop("Austria")
print(capital)

OUTPUT:

Vienna
capitals

OUTPUT:

{'Germany': 'Berlin', 'Netherlands': 'Amsterdam', 'Switzerland': 'Bern'}

If we try to find out the capital of France in the previous example, we raise a KeyError. To prevent these errors, there is an elegant way. The method pop() has an optional second parameter, which can be used as a default value, if key is not in the dictionary:

capital = capitals.pop("France", "Paris")
print(capital)

OUTPUT:

Paris
capital = capitals.pop("Germany", "München")
print(capital)

OUTPUT:

Berlin

popitem

popitem() is a method of dict, which doesn't take any parameter and removes and returns a (key,value) pair as a 2-tuple. If popitem() is applied on an empty dictionary, a KeyError will be raised.

Pairs are returned in LIFO (last-in, first-out) order.

capitals = {"Springfield": "Illinois", 
            "Augusta": "Maine", 
            "Boston": "Massachusetts", 
            "Lansing": "Michigan", 
            "Albany": "New York", 
            "Olympia": "Washington", 
            "Toronto": "Ontario"}
(city, state) = capitals.popitem()
(city, state)

OUTPUT:

('Toronto', 'Ontario')
print(capitals.popitem())

OUTPUT:

('Olympia', 'Washington')
print(capitals.popitem())
help(dict.popitem)

OUTPUT:

Help on method_descriptor:

popitem(self, /)
    Remove and return a (key, value) pair as a 2-tuple.
    
    Pairs are returned in LIFO (last-in, first-out) order.
    Raises KeyError if the dict is empty.
print(capitals.popitem())
print(capitals.popitem())

Accessing Non-existing Keys

If you try to access a key which doesn't exist, you will get an error message:

locations = {"Stuttgart": "Baden-Württemberg", 
             "Hamburg": "Hamburg", 
             "München": "Bayern", 
             "Köln": "Nordrhein-Westfalen"}
locations["Saarbrücken"]

OUTPUT:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[10], line 5
      1 locations = {"Stuttgart": "Baden-Württemberg", 
      2              "Hamburg": "Hamburg", 
      3              "München": "Bayern", 
      4              "Köln": "Nordrhein-Westfalen"}
----> 5 locations["Saarbrücken"]

KeyError: 'Saarbrücken'

To prevent getting an exception we can check first, if the city is in:

location = "Saarbrücken"
if location in locations:
    print(locations[location])
else:
    print(f"{location} is not a key of locations!")

OUTPUT:

Saarbrücken is not a key of locations!

Another method to access the values via the key consists in using the get() method. get() is not raising an error, if an index doesn't exist. In this case it will return None. It's also possible to set a default value, which will be returned, if an index doesn't exist:

locations.get("Saarbrücken") # return None

Another example:

proj_language = {"proj1":"Python", "proj2":"Perl", "proj3":"Java"}
proj_language["proj1"]

OUTPUT:

'Python'
proj_language.get("proj2")

OUTPUT:

'Perl'
proj_language.get("proj4")
print(proj_language.get("proj4")) 

OUTPUT:

None
# setting a default value:
proj_language.get("proj4", "Python")

OUTPUT:

'Python'

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Enrol here

Important Methods

A dictionary can be copied with the method copy():

copy()

words = {'house': 'Haus', 'cat': 'Katze'}
w = words.copy()
words["cat"]="chat"
print(w)
print(words)

This copy is a shallow copy, not a deep copy. If a value is a complex data type like a list, for example, in-place changes in this object have effects on the copy as well:

trainings = { "course1":{"title":"Python Training Course for Beginners", 
                         "location":"Frankfurt", 
                         "trainer":"Steve G. Snake"},
              "course2":{"title":"Intermediate Python Training",
                         "location":"Berlin",
                         "trainer":"Ella M. Charming"},
              "course3":{"title":"Python Text Processing Course",
                         "location":"München",
                         "trainer":"Monica A. Snowdon"}
              }

trainings2 = trainings.copy()

trainings["course2"]["title"] = "Perl Training Course for Beginners"
print(trainings2)

If we check the output, we can see that the title of course2 has been changed not only in the dictionary training but in trainings2 as well.

Everything works the way you expect it, if you assign a new value, i.e. a new object, to a key:

trainings = { "course1": {"title": "Python Training Course for Beginners", 
                         "location": "Frankfurt", 
                         "trainer": "Steve G. Snake"},
              "course2": {"title": "Intermediate Python Training",
                         "location": "Berlin",
                         "trainer": "Ella M. Charming"},
              "course3": {"title": "Python Text Processing Course",
                         "location": "München",
                         "trainer": "Monica A. Snowdon"}
              }

trainings2 = trainings.copy()

trainings["course2"] = {"title": "Perl Seminar for Beginners",
                         "location": "Ulm",
                         "trainer": "James D. Morgan"}
print(trainings2["course2"])

If you want to understand the reason for this behaviour, we recommend our chapter "Shallow and Deep Copy".

clear()

The content of a dictionary can be cleared with the method clear(). The dictionary is not deleted, but set to an empty dictionary:

w.clear()
print(w)

Update: Merging Dictionaries

What about concatenating dictionaries, like we did with lists? There is someting similar for dictionaries: the update method update() merges the keys and values of one dictionary into another, overwriting values of the same key:

knowledge = {"Frank": {"Perl"}, "Monica":{"C","C++"}}
knowledge2 = {"Guido":{"Python"}, "Frank":{"Perl", "Python"}}
knowledge.update(knowledge2)
knowledge

OUTPUT:

{'Frank': {'Perl', 'Python'}, 'Monica': {'C', 'C++'}, 'Guido': {'Python'}}

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Enrol here

Iterating over a Dictionary

No method is needed to iterate over the keys of a dictionary:

d = {"a":123, "b":34, "c":304, "d":99}
for key in d:
     print(key) 

OUTPUT:

a
b
c
d

However, it's possible to use the method keys(), we will get the same result:

for key in d.keys():
     print(key) 

OUTPUT:

a
b
c
d

The method values() is a convenient way for iterating directly over the values:

for value in d.values():
    print(value)

OUTPUT:

123
34
304
99

The above loop is logically equivalent to the following one:

for key in d:
    print(d[key])

OUTPUT:

123
34
304
99

We said logically, because the second way is less efficient!

If you are familiar with the timeit possibility of ipython, you can measure the time used for the two alternatives:

%%timeit  d = {"a":123, "b":34, "c":304, "d":99}
for value in d.values():
    x = value

OUTPUT:

118 ns ± 17.4 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
%%timeit  d = {"a":123, "b":34, "c":304, "d":99}
for key in d.keys():
    x = d[key]

OUTPUT:

154 ns ± 22.9 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

Exercises

Exercise 1

Write a function dict_merge_sum that takes two dictionaries d1 and d2 as parameters. The values of both dictionaries are numerical. The function should return the merged sum dictionary m of those dictionaries. If a key k is both in d1 and d2, the corresponding values will be added and included in the dictionary m If k is only contained in one of the dictionaries, the k and the corresponding value will be included in m

Exercise 2

Given is the following simplified data of a supermarket:

supermarket = { "milk": {"quantity": 20, "price": 1.19},
               "biscuits":  {"quantity": 32, "price": 1.45},
               "butter":  {"quantity": 20, "price": 2.29},
               "cheese":  {"quantity": 15, "price": 1.90},
               "bread":  {"quantity": 15, "price": 2.59},
               "cookies":  {"quantity": 20, "price": 4.99},
               "yogurt": {"quantity": 18, "price": 3.65},
               "apples":  {"quantity": 35, "price": 3.15},
               "oranges":  {"quantity": 40, "price": 0.99},
               "bananas": {"quantity": 23, "price": 1.29}}

To be ready for an imminent crisis you decide to buy everything. This isn't particularly social behavior, but for the sake of the task, let's imagine it. The question is how much will you have to pay?

Exercise 3

Exercise 4

Given is the island of Vannoth

Island with fake Cities

Create a dictionary, where we get for every city of Vannoth the distance to the capital city of Rogeburgh

Exercise 5

Create a dictionary where you can get the distance between two arbitrary cities

Exercise 6

A salesperson takes the following route: Port Carol to Rogerburgh to Smithstad to Scottshire to Clarkhaven to Dixonshire to Port Carol. How many kilometres did the salesperson drive?

To solve this dictionary exercise it will be optimal, if you are familiar with 'for' or while loops in Python. We recommend to work through the corresponding chapters in our python course.

Exercise 7

cities = ['Stuttgart', 'Karlsruhe', 'Mannheim', 'Freiburg', 'Heidelberg', 'Heilbronn']
areas = [207.35, 173.46, 144.96, 153.07, 108.83, 99.88]  # Areas are in square kilometers
populations = [634830, 313092, 309370, 230241, 160355, 125960]

Write Pyhton code to create a dictionary with the city names as keys and the corresponding areas and population numbers as values.

Exercise 8

Student Gradebook using Nested Dictionaries

To solve this exercise you have to be familiar with functions in Python, see our introduction on functions in our python course.

Create a Python program to manage a student gradebook using nested dictionaries. Follow these steps to implement the functionality:

  1. Initialize an empty dictionary to serve as the gradebook.
  2. Implement a function add_student(gradebook, student_name) that takes the gradebook dictionary and a student name as input and adds an empty dictionary for the student's grades to the gradebook.
  3. Implement a function add_grade(gradebook, student_name, subject, grade) that takes the gradebook dictionary, a student name, a subject name, and a grade as input. This function should add the grade for the specified subject to the grades dictionary of the specified student in the gradebook.
  4. Implement a function calculate_average_grade(gradebook, student_name) that takes the gradebook dictionary and a student name as input and calculates the average grade for the specified student.
  5. Implement a function display_gradebook(gradebook) that takes the gradebook dictionary as input and displays the contents of the gradebook, including student names, subjects, and grades, as well as the average grade for each student.

Use the provided sample usage to demonstrate the functionality of your program.

Note: You may assume that each student will have unique names, and subjects may have duplicate names but within the context of different students.

An example dictionary may look like this:

{'Alice': {'Math': 85, 'Science': 90, 'History': 88},
 'Bob': {'Math': 78, 'Science': 92, 'History': 85}}

OUTPUT:

{'Alice': {'Math': 85, 'Science': 90, 'History': 88},
 'Bob': {'Math': 78, 'Science': 92, 'History': 85}}

Sample usage looks like this:

gradebook = {}

# Add students to the gradebook
add_student(gradebook, "Alice")
add_student(gradebook, "Bob")

# Add grades for subjects
add_grade(gradebook, "Alice", "Math", 85)
add_grade(gradebook, "Alice", "Science", 90)
add_grade(gradebook, "Alice", "History", 88)

add_grade(gradebook, "Bob", "Math", 78)
add_grade(gradebook, "Bob", "Science", 92)
add_grade(gradebook, "Bob", "History", 85)

# Display the gradebook
display_gradebook(gradebook)

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Enrol here

Solutions

Solution to Exercise 1

We offer two solutions to this exercise:

The first one is only using things you will have learned, if you followed our Python course sequentially. The second solution uses techniques which will be covered later in our tutorial.

First solution:

def dict_merge_sum(d1, d2):
    """ Merging and calculating the sum of two dictionaries: 
    Two dicionaries d1 and d2 with numerical values and
    possibly disjoint keys are merged and the values are added if
    the exist in both values, otherwise the missing value is taken to
    be 0"""
    
    merged_sum = d1.copy()
    for key, value in d2.items():
        if key in d1:
            d1[key] += value
        else:
            d1[key] = value
    
    return merged_sum

d1 = dict(a=4, b=5, d=8)
d2 = dict(a=1, d=10, e=9)

dict_merge_sum(d1, d2)

OUTPUT:

{'a': 4, 'b': 5, 'd': 8}

Second solution:

def dict_sum(d1, d2):
    """  Merging and calculating the sum of two dictionaries: 
    Two dicionaries d1 and d2 with numerical values and
    possibly disjoint keys are merged and the values are added if
    the exist in both values, otherwise the missing value is taken to
    be 0"""
    
    return { k: d1.get(k, 0) + d2.get(k, 0) for k in set(d1) | set(d2) }

d1 = dict(a=4, b=5, d=8)
d2 = dict(a=1, d=10, e=9)

dict_merge_sum(d1, d2)

OUTPUT:

{'a': 4, 'b': 5, 'd': 8}

Solution to Exercise 2:

supermarket = { "milk": {"quantity": 20, "price": 1.19},
               "biscuits":  {"quantity": 32, "price": 1.45},
               "butter":  {"quantity": 20, "price": 2.29},
               "cheese":  {"quantity": 15, "price": 1.90},
               "bread":  {"quantity": 15, "price": 2.59},
               "cookies":  {"quantity": 20, "price": 4.99},
               "yogurt": {"quantity": 18, "price": 3.65},
               "apples":  {"quantity": 35, "price": 3.15},
               "oranges":  {"quantity": 40, "price": 0.99},
               "bananas": {"quantity": 23, "price": 1.29}}

total_value = 0
for article in supermarket:
    quantity = supermarket[article]["quantity"]
    price = supermarket[article]["price"]
    total_value += quantity * price

print(f"The total price for buying everything: {total_value:7.2f}")

OUTPUT:

The total price for buying everything:  528.37

Solution to Exercise 3:

supermarket = { "milk": {"quantity": 20, "price": 1.19},
               "biscuits":  {"quantity": 32, "price": 1.45},
               "butter":  {"quantity": 20, "price": 2.29},
               "cheese":  {"quantity": 15, "price": 1.90},
               "bread":  {"quantity": 15, "price": 2.59},
               "cookies":  {"quantity": 20, "price": 4.99},
               "yogurt": {"quantity": 18, "price": 3.65},
               "apples":  {"quantity": 35, "price": 3.15},
               "oranges":  {"quantity": 40, "price": 0.99},
               "bananas": {"quantity": 23, "price": 1.29}            
              }



customers = ["Frank", "Mary", "Paul"]
shopping_lists = { 
   "Frank" : [('milk', 5), ('apples', 5), ('butter', 1), ('cookies', 1)],
   "Mary":  [('apples', 2), ('cheese', 4), ('bread', 2), ('pears', 3), 
             ('bananas', 4), ('oranges', 1), ('cherries', 4)],
   "Paul":  [('biscuits', 2), ('apples', 3), ('yogurt', 2), ('pears', 1), 
             ('butter', 3), ('cheese', 1), ('milk', 1), ('cookies', 4)]}

    
# filling the carts
carts = {}
for customer in customers:
    carts[customer] = []
    for article, quantity in shopping_lists[customer]:
        if article in supermarket:
            if supermarket[article]["quantity"] < quantity:
                quantity = supermarket[article]["quantity"]
            if quantity:
                supermarket[article]["quantity"] -= quantity
                carts[customer].append((article, quantity))
for customer in customers:                            
     print(carts[customer])
            
            
print("checkout")
for customer in customers:
    print("\ncheckout for " + customer + ":")
    total_sum = 0
    for name, quantity in carts[customer]:
        unit_price = supermarket[name]["price"]
        item_sum = quantity * unit_price
        print(f"{quantity:3d} {name:12s} {unit_price:8.2f} {item_sum:8.2f}")
        total_sum += item_sum
    print(f"Total sum:             {total_sum:11.2f}")

OUTPUT:

[('milk', 5), ('apples', 5), ('butter', 1), ('cookies', 1)]
[('apples', 2), ('cheese', 4), ('bread', 2), ('bananas', 4), ('oranges', 1)]
[('biscuits', 2), ('apples', 3), ('yogurt', 2), ('butter', 3), ('cheese', 1), ('milk', 1), ('cookies', 4)]
checkout

checkout for Frank:
  5 milk             1.19     5.95
  5 apples           3.15    15.75
  1 butter           2.29     2.29
  1 cookies          4.99     4.99
Total sum:                   28.98

checkout for Mary:
  2 apples           3.15     6.30
  4 cheese           1.90     7.60
  2 bread            2.59     5.18
  4 bananas          1.29     5.16
  1 oranges          0.99     0.99
Total sum:                   25.23

checkout for Paul:
  2 biscuits         1.45     2.90
  3 apples           3.15     9.45
  2 yogurt           3.65     7.30
  3 butter           2.29     6.87
  1 cheese           1.90     1.90
  1 milk             1.19     1.19
  4 cookies          4.99    19.96
Total sum:                   49.57

Alternative solution to exercise 3, in which we create the chopping lists randomly:

import random

supermarket = { "milk": {"quantity": 20, "price": 1.19},
               "biscuits":  {"quantity": 32, "price": 1.45},
               "butter":  {"quantity": 20, "price": 2.29},
               "cheese":  {"quantity": 15, "price": 1.90},
               "bread":  {"quantity": 15, "price": 2.59},
               "cookies":  {"quantity": 20, "price": 4.99},
               "yogurt": {"quantity": 18, "price": 3.65},
               "apples":  {"quantity": 35, "price": 3.15},
               "oranges":  {"quantity": 40, "price": 0.99},
               "bananas": {"quantity": 23, "price": 1.29}            
              }


articles4shopping_lists = list(supermarket.keys()) + ["pears", "cherries"]

max_articles_per_customer = 5 # not like a real supermarket :-)
customers = ["Frank", "Mary", "Paul", "Jennifer"]
shopping_lists = {}
for customer in customers:
    no_of_items = random.randint(1, len(articles4shopping_lists))
    shopping_lists[customer] = []
    for article_name in random.sample(articles4shopping_lists, no_of_items):
        quantity = random.randint(1, max_articles_per_customer)
        shopping_lists[customer].append((article_name, quantity))

# let's look at the shopping lists
print("Shopping lists:")        
for customer in customers:     
    print(customer + ":  " + str(shopping_lists[customer]))

# filling the carts
carts = {}
for customer in customers:
    carts[customer] = []
    for article, quantity in shopping_lists[customer]:
        if article in supermarket:
            if supermarket[article]["quantity"] < quantity:
                quantity = supermarket[article]["quantity"]
            if quantity:
                supermarket[article]["quantity"] -= quantity
                carts[customer].append((article, quantity))


print("\nCheckout")
for customer in customers:
    print("\ncheckout for " + customer + ":")
    total_sum = 0
    for name, quantity in carts[customer]:
        unit_price = supermarket[name]["price"]
        item_sum = quantity * unit_price
        print(f"{quantity:3d} {name:12s} {unit_price:8.2f} {item_sum:8.2f}")
        total_sum += item_sum
    print(f"Total sum:             {total_sum:11.2f}")

OUTPUT:

Shopping lists:
Frank:  [('bananas', 1), ('yogurt', 4), ('biscuits', 2), ('butter', 2)]
Mary:  [('oranges', 4), ('bread', 3), ('cookies', 5), ('yogurt', 5), ('bananas', 4), ('biscuits', 5), ('pears', 4), ('cheese', 3), ('milk', 5), ('apples', 5)]
Paul:  [('cheese', 2), ('bread', 5), ('biscuits', 1), ('pears', 2), ('cherries', 2)]
Jennifer:  [('milk', 5), ('oranges', 3)]

Checkout

checkout for Frank:
  1 bananas          1.29     1.29
  4 yogurt           3.65    14.60
  2 biscuits         1.45     2.90
  2 butter           2.29     4.58
Total sum:                   23.37

checkout for Mary:
  4 oranges          0.99     3.96
  3 bread            2.59     7.77
  5 cookies          4.99    24.95
  5 yogurt           3.65    18.25
  4 bananas          1.29     5.16
  5 biscuits         1.45     7.25
  3 cheese           1.90     5.70
  5 milk             1.19     5.95
  5 apples           3.15    15.75
Total sum:                   94.74

checkout for Paul:
  2 cheese           1.90     3.80
  5 bread            2.59    12.95
  1 biscuits         1.45     1.45
Total sum:                   18.20

checkout for Jennifer:
  5 milk             1.19     5.95
  3 oranges          0.99     2.97
Total sum:                    8.92

Solution to Exercise 4:

distance_from_capital = {'Rogerburgh': 0,
                         'Smithstad': 5.2, 
                         'Scottshire': 12.3, 
                         'Clarkhaven': 14.9, 
                         'Dixonshire': 12.7, 
                         'Port Carol': 3.4}

Solution to Exercise 5:

# we implement the distance matrix a.k.a connectivity matrix as a Python dictionary
distance_matrix = {
         'Rogerburgh': {'Rogerburgh': 0, 'Smithstad': 5.2, 'Scottshire': 12.3,
                        'Clarkhaven': 14.9, 'Dixonshire': 12.7, 'Port Carol': 3.4},
         'Smithstad': {'Rogerburgh': 5.2, 'Smithstad': 0, 'Scottshire': 8.4, 
                       'Clarkhaven': 13.9, 'Dixonshire': 14.3, 'Port Carol': 8.6},
         'Scottshire': {'Rogerburgh': 12.3, 'Smithstad': 8.4,  'Scottshire': 0,
                        'Clarkhaven': 20.1, 'Dixonshire': 21.6, 'Port Carol': 6.5},
         'Clarkhaven': {'Rogerburgh': 14.9, 'Smithstad': 13.9, 'Scottshire': 20.1,
                        'Clarkhaven': 0, 'Dixonshire': 11.9, 'Port Carol': 18.7},
         'Dixonshire': {'Rogerburgh': 12.7, 'Smithstad': 14.3, 'Scottshire': 21.6,
                        'Clarkhaven': 11.9, 'Dixonshire': 0, 'Port Carol': 15.3},
         'Port Carol': {'Rogerburgh': 3.4, 'Smithstad': 8.6, 'Scottshire': 8.6,
                        'Clarkhaven': 18.7, 'Dixonshire': 15.3, 'Port Carol': 0}}

Solution to Exercise 6

t = "Port Carol to Rogerburgh to Smithstad to Scottshire to Clarkhaven to Dixonshire to Port Carol"
cities = t.split(' to ')
cities

OUTPUT:

['Port Carol',
 'Rogerburgh',
 'Smithstad',
 'Scottshire',
 'Clarkhaven',
 'Dixonshire',
 'Port Carol']
total_distance = 0
for i in range(len(cities)-1):
    distance = distance_matrix[cities[i]][cities[i+1]]
    txt = f"From {cities[i]} to {cities[i+1]}"
    print(f"{txt:30s}: {distance:5.1f}")
    total_distance += distance
print("="* 40)
print(f'{"The salesperson travelled":30s}: {total_distance:5.1f}')

OUTPUT:

From Port Carol to Rogerburgh :   3.4
From Rogerburgh to Smithstad  :   5.2
From Smithstad to Scottshire  :   8.4
From Scottshire to Clarkhaven :  20.1
From Clarkhaven to Dixonshire :  11.9
From Dixonshire to Port Carol :  15.3
========================================
The salesperson travelled     :  64.3

Solution to exercise 7

cities = ['Stuttgart', 'Karlsruhe', 'Mannheim', 'Freiburg', 'Heidelberg', 'Heilbronn']
areas = [207.35, 173.46, 144.96, 153.07, 108.83, 99.88]  # Areas are in square kilometers
populations = [634830, 313092, 309370, 230241, 160355, 125960]

city_info_dict = {city: (area, population) for city, area, population in zip(cities, areas, populations)}

print(city_info_dict)

OUTPUT:

{'Stuttgart': (207.35, 634830), 'Karlsruhe': (173.46, 313092), 'Mannheim': (144.96, 309370), 'Freiburg': (153.07, 230241), 'Heidelberg': (108.83, 160355), 'Heilbronn': (99.88, 125960)}

Solution to exercise 8

def add_student(gradebook, student_name):
    gradebook[student_name] = {}

def add_grade(gradebook, student_name, subject, grade):
    if student_name in gradebook:
        gradebook[student_name][subject] = grade
    else:
        print("Student '{}' not found.".format(student_name))

def calculate_average_grade(gradebook, student_name):
    if student_name in gradebook:
        grades = gradebook[student_name].values()
        average_grade = sum(grades) / len(grades)
        return average_grade
    else:
        print("Student '{}' not found.".format(student_name))
        return None

def display_gradebook(gradebook):
    print("Gradebook:")
    for student, grades in gradebook.items():
        print(student + ":")
        for subject, grade in grades.items():
            print("- {}: {}".format(subject, grade))
        average_grade = calculate_average_grade(gradebook, student)
        if average_grade is not None:
            print("  Average grade: {:.2f}".format(average_grade))
        print()


# Example usage:
gradebook = {}
add_student(gradebook, "Alice")
add_grade(gradebook, "Alice", "Math", 85)
add_grade(gradebook, "Alice", "Science", 90)
add_grade(gradebook, "Alice", "History", 88)

add_student(gradebook, "Bob")
add_grade(gradebook, "Bob", "Math", 78)
add_grade(gradebook, "Bob", "Science", 92)
add_grade(gradebook, "Bob", "History", 85)

display_gradebook(gradebook)

OUTPUT:

Gradebook:
Alice:
- Math: 85
- Science: 90
- History: 88
  Average grade: 87.67

Bob:
- Math: 78
- Science: 92
- History: 85
  Average grade: 85.00

Footnotes

1 The city of Constance, also known as Konstanz in German, holds historical significance as it was the site of the Council of Constance from 1414 to 1418. This council played a crucial role in resolving the Papal Schism, which involved multiple claimants to the papacy. Additionally, Constance is located at the western end of Lake Constance (Bodensee), where Germany, Switzerland, and Austria meet, making it a picturesque and culturally rich destination.

2 If you would like to learn more about how iterators work and how to use them, we recommend that you go through our Iterators and Generators chapter.

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Upcoming online Courses

Enrol here