15. Comprehensions#

In this notebook, we cover the following subjects:

  • Why Comprehensions?;

  • List Comprehension;

  • Set and Dictionary Comprehension;

  • Nested Comprehensions.


# To enable type hints for lists, dicts, tuples, and sets we need to import the following:
from typing import List, Dict, Tuple, Set

15.1. Why Comprehensions?#

When you’re creating sequences, like lists or sets, it can be tedious and repetitive. Luckily, comprehensions can make that process a lot easier.

A comprehension is an expression that constructs a data structure based on some iterable. In this notebook, we will cover the three main types of comprehensions: list, dictionary, and set comprehensions. Examples make comprehensions much clearer, so let’s dive into the first one.

15.2. List Comprehension#

Imagine you’re given the task of creating a list with all the squares of the numbers 0 through 9. Most probably ou would solve this task using a traditional loop in the following way:

squared_numbers: List[int] = []
for number in range(10):
    squared_numbers.append(number ** 2)

print(squared_numbers)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

This looks like a successful execution, doesn’t it? However, you can do this in a more compact way using comprehensions. The syntax for list comprehensions is as follows:

list_comprehension = [expression for item in iterable]

Now, if we apply this to our task, it looks like this:

squared_numbers: List[int] = [number ** 2 for number in range(10)]

print(squared_numbers)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

See? The same result is achieved in an easier way that also requires less effort. The code snippets can each be linked to the components of the comprehension:

  • Expression: number ** 2 – This is where the magic happens. It’s the operation you want to apply to each element in the iterable.

  • Item: number – This is your placeholder, representing each element that you take from the sequence to process.

  • Iterable: range(10) – This is the original sequence you’re iterating over, one by one, to apply the operation.

Note

A list comprehension is easily recognized by the square brackets [], which indicate that you’re creating a list.

15.2.1. Selective Inclusion in a Comprehension#

You can also selectively include items in your list by adding an if clause. This adjusts the syntax we used earlier like this:

list_comprehension = [expression for item in iterable if condition]

For example, we can adjust our previous task by specifying that we only want to store the squares of the odd numbers in the list.

odd_squares: List[int] = [number ** 2 for number in range(10) if number % 2 != 0]

print(odd_squares)
[1, 9, 25, 49, 81]

15.2.2. Conditional Expression within a Comprehension#

Sometimes, we want to distinguish between elements or apply different operations in a sequence rather than just filtering. For this, we use a conditional expression in a list comprehension. This lets us choose between two expressions based on a condition. The syntax is:

[true_expression if condition else false_expression for item in iterable]

Here, true_expression if condition else false_expression represents the conditional expression. It determines which expression to use depending on whether the condition is True or False. Let’s break it down:

  • true_expression: If the condition evaluates to True, this is the expression used.

  • condition: This is the condition that decides which expression to use.

  • false_expression: This is the expression used when the condition evaluates to False.

Let’s look at an example. Imagine, your task is to create a new list from a list of integers where each number is squared if it’s even, and 0 if it’s odd.

# Original list of integers
numbers: List[int] = [12, 45, 7, 23, 89, 34, 56, 78, 9, 67]
print("Original list:", numbers)

# List comprehension to square even numbers and replace odd numbers with 0
squared_evens_or_zero: List[int] = [number ** 2 if number % 2 == 0 else 0 for number in numbers]
print("Squared evens or zero:", squared_evens_or_zero)
Original list: [12, 45, 7, 23, 89, 34, 56, 78, 9, 67]
Squared evens or zero: [144, 0, 0, 0, 0, 1156, 3136, 6084, 0, 0]

Note

Remember that an expression is a combination of values, variables, and operators, but a value by itself is also considered an expression.

In this example, we apply an operation when a number is even (condition evaluates to True) and replace it with a specific value, namely 0, when the condition is False. We can also choose a different operation for the False condition. For instance, instead of replacing each odd number with 0, we could triple it if it’s odd.

# List of integers
numbers: List[int] = [12, 45, 7, 23, 89, 34, 56, 78, 9, 67]
print("Original list:", numbers)

# List comprehension to square even numbers and triple odd numbers
squared_evens_or_triple_odds: List[int] = [number ** 2 if number % 2 == 0 else number * 3 for number in numbers]
print("Squared evens or tripled odds:", squared_evens_or_triple_odds)
Original list: [12, 45, 7, 23, 89, 34, 56, 78, 9, 67]
Squared evens or tripled odds: [144, 135, 21, 69, 267, 1156, 3136, 6084, 27, 201]

15.3. Set and Dictionary Comprehension#

15.3.1. Set Comprehension#

Set comprehension mirrors list comprehension but produces a set instead. The syntax is nearly identical, with the main difference being that sets use curly braces {}:

set_comprehension = {expression for item in iterable if condition}

Let’s look at an example. Imagine we have a list of words, and we want to create a set that contains all the words longer than three characters, converted to uppercase.

plants = ["rose", "lily", "sunflower", "oak", "fern", "cactus"]

uppercase_plants = {plant.upper() for plant in plants if len(plant) > 3}

print(uppercase_plants)
{'ROSE', 'SUNFLOWER', 'FERN', 'LILY', 'CACTUS'}

15.3.2. Dictionary Comprehension#

Just like list and set comprehensions, dictionary comprehensions are easiest to understand through an example. The syntax is as follows:

dict_comprehension = {key_expression: value_expression for item in iterable}

Let’s jump straight to an example. For instance, if you want to create a dictionary where the keys are numbers from 1 to 5 and the values are their squares, you can achieve this with a dictionary comprehension:

# Dictionary of numbers and their squares
squares_dict: Dict[int, int] = {x: x ** 2 for x in range(1, 6)}

print(squares_dict)
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

As can be seen, the main difference between list and set comprehensions and dictionary comprehensions is how elements are defined. In dictionary comprehensions, you specify both a key and a value, with each being defined by an expression.

Note

Conditional expressions and selective inclusion can also be used in dictionary and set comprehensions.

15.4. Nested Comprehensions#

If you need to select items after transforming them, you can use a nested comprehension. In this approach, the inner comprehension is evaluated first, and the result is then used by the outer comprehension. The syntax for nested list comprehensions is:

nested_list_comprehension = [[expression for item in inner_iterable] for item in outer_iterable]

Here, the inner comprehension processes items from inner_iterable, and the outer comprehension uses the resulting lists to construct the final structure.

For instance, let’s say you want to create a 3x3 matrix represented as a list of lists where each element is the product (multiplication) of its row and column indices (using 1 as the starting index):

matrix: List[List[int]] = [[x * y for y in range(1,4)] for x in range(1,4)]

print(matrix)
[[1, 2, 3], [2, 4, 6], [3, 6, 9]]

In this example, you have a nested comprehension. The outer comprehension constructs the rows of the matrix, and the inner comprehension [i * j for j in range(1, 4)] generates the elements within each row by computing the products of i and j.

Note: a matrix looks as follows:

                                | 1  2  3 |
                                | 4  5  6 |
                                | 7  8  9 |

Which can be represented in Python as follows:

                                matrix = [
                                    [1, 2, 3],
                                    [4, 5, 6],
                                    [7, 8, 9]
                                ]

Note

You can use nested comprehensions with sets and dictionaries just as you would with lists.

15.5. Exercises#

Let’s practice! Mind that each exercise is designed with multiple levels to help you progressively build your skills. Level 1 is the foundational level, designed to be straightforward so that everyone can successfully complete it. In Level 2, we step it up a notch, expecting you to use more complex concepts or combine them in new ways. Finally, in Level 3, we get closest to exam level questions, but we may use some concepts that are not covered in this notebook. However, in programming, you often encounter situations where you’re unsure how to proceed. Fortunately, you can often solve these problems by starting to work on them and figuring things out as you go. Practicing this skill is extremely helpful, so we highly recommend completing these exercises.

For each of the exercises, make sure to add a docstring and type hints, and do not import any libraries unless specified otherwise.

15.5.1. Exercise 1#

Let’s practise with comprehensions! Imagine you’re the coach of a new football team and need to get to know your players. You have stats showing each player’s goal-scoring record throughout their career.

Level 1: Your task is to create a function called average_goals(). This function should accept a list of dictionaries. Each dictionary represents a player and contains their name and a list of integers showing the number of goals scored each season. You need to calculate the average number of goals scored by each player over their entire career and store this in a new dictionary. In this dictionary, each key will be a player’s name, and the value will be their average number of goals per season, rounded to one decimal place. Use comprehensions to create the resulting dictionary. Print the result outside the function in a clear and readable format.

Example input: you pass this argument to the parameter in the function call.

players: List[Dict[str, List[int]]] = [
    {'name': 'Vivianne Miedema', 'goals': [23, 20, 22, 17, 25]},
    {'name': 'Ellen White', 'goals': [15, 12, 11, 14, 16]},
    {'name': 'Marta', 'goals': [10, 8, 9, 12, 11]},
    {'name': 'Alexandra Popp', 'goals': [14, 10, 12, 9, 13]}
]

Example return value:

{
    'Vivianne Miedema': 21.4,
    'Ellen White': 13.6,
    'Marta': 10.0,
    'Alexandra Popp': 11.6
}

Example output:

Vivianne Miedema: 21.4
Ellen White: 13.6
Marta: 10.0
Alexandra Popp: 11.6
# TODO.

Level 2: As a football analytics intern, you are tasked with calculating the areas of junior league football pitches, represented by squares of prime dimensions from 2 to 19. Create a list comprehension prime_square_areas that captures these areas.

Example input:

range(2,20)

Example output:

[4, 9, 25, 49, 121, 169, 289, 361]
# TODO

Level 3: Advance your pitch area analysis by generating a list of unique pitch areas, ensuring that no two pitches have the same remainder when divided by 10. Use a list comprehension named unique_remainder_areas to derive the areas from prime dimensions between 2 and 19 (computed in level 2), Moreover, filter them to keep only the first occurrence of each unique remainder.

Example: If the areas calculated are [4, 9, 25, 49, 121, 169], the remainders when divided by 10 are [4, 9, 5, 9, 1, 9]. The unique first occurrences are [4, 9, 5, 1], corresponding to areas [4, 9, 25, 121].

Example input:

range(2,20)

Example output:

[4, 9, 25, 121

15.5.2. Exercise 2#

Level 1: You are tasked with managing Christmas gifts for children. Each gift has a certain value (in dollars) based on the number of candies in a box, cubed. Create a set comprehension named gift_values that calculates the value of each gift box, where the number of candies ranges from 1 to 10.

Example input:

range(1, 11)

Example output:

{1, 8, 27, 64, 125, 216, 343, 512, 729, 1000}
# TODO

Level 2: Santa’s elves have created a list of children’s names along with the number of candies each child wishes for in their gift. Create a dictionary comprehension named wishlist_dict that maps each child’s name to their desired number of candies, but only include those children who want more than 5 candies.

Example input:

wishlist: Dict[str, int] = {'Alice': 4, 'Bob': 6, 'Charlie': 9, 'Daisy': 3, 'Ethan': 8}

Example output:

{'Bob': 6, 'Charlie': 9, 'Ethan': 8}
# TODO

Level 3: Santa is decorating the Christmas tree and wants to use different colored ornaments. The elves have identified the ornaments by color names. You need to create a nested data structure—specifically, a dictionary where each key is an ornament color, and the value is a set of unique letters used in the color’s name. Use a dictionary comprehension named ornament_letters.

Example input:

ornament_colors: List[str] = ['red', 'gold', 'silver', 'blue']

Example output:

{'red': {'d', 'e', 'r'}, 'gold': {'g', 'l', 'o', 'd'}, 'silver': {'s', 'i', 'l', 'v', 'e', 'r'}, 'blue': {'b', 'l', 'u', 'e'}}
# TODO

15.5.3. Exercise 3#

Level 1: You have a list of designer brands and their popularity ratings (on a scale from 1 to 10). Use a dictionary comprehension named brand_popularity to create a dictionary where each brand is a key, and its rating is the value. If the rating is 8 or above, mark the brand as "Highly Popular"; otherwise, mark it as "Moderately Popular".

Example input:

brands: Dict[str, int] = {'Gucci': 9, 'Prada': 7, 'Louis Vuitton': 10, 'Chanel': 8, 'Balenciaga': 6}

Example output:

{'Gucci': 'Highly Popular', 'Prada': 'Moderately Popular', 'Louis Vuitton': 'Highly Popular', 'Chanel': 'Highly Popular', 'Balenciaga': 'Moderately Popular'}
# TODO

Level 2: During the holiday season, designer brands are offering discounts based on their popularity. Brands rated 8 or higher will have a 20% discount, while others will have a 10% discount. Use a dictionary comprehension named brand_discounts to create a dictionary where each brand’s value is its discount percentage. This dictionary should be derived from the popularity ratings provided.

Example input:

brands: Dict[str, int] = {'Gucci': 9, 'Prada': 7, 'Louis Vuitton': 10, 'Chanel': 8, 'Balenciaga': 6}

Example output:

{'Gucci': 20%, 'Prada': 10%, 'Louis Vuitton': 20%, 'Chanel': 20%, 'Balenciaga': 10%}
# TODO

Level 3: During the holiday season, the brands are creating gift collections. The collection is made up of items whose total value must be below a threshold. Create a nested dictionary named brand_collections where each brand is a key, and the value is another dictionary of item names and prices. Only include items whose price, after applying the appropriate discount (from Level 2), is below $500. Use a dictionary comprehension with a nested comprehension to accomplish this.

Example input:

brand_items: Dict[Dict[str, int]] = {
    'Gucci': {'Bag': 600, 'Shoes': 450, 'Belt': 300},
    'Prada': {'Sunglasses': 200, 'Hat': 100, 'Shoes': 500},
    'Louis Vuitton': {'Bag': 1000, 'Wallet': 400, 'Scarf': 150},
    'Chanel': {'Perfume': 300, 'Handbag': 700, 'Necklace': 450},
    'Balenciaga': {'Sneakers': 400, 'Hoodie': 550, 'Cap': 200}
}
brand_discounts: Dict[str, int] = {'Gucci': 20, 'Prada': 10, 'Louis Vuitton': 20, 'Chanel': 20, 'Balenciaga': 10}

Example output:

{
    'Gucci': {'Shoes': 360.0, 'Belt': 240.0},
    'Prada': {'Sunglasses': 180.0, 'Hat': 90.0},
    'Louis Vuitton': {'Wallet': 320.0, 'Scarf': 120.0},
    'Chanel': {'Perfume': 240.0, 'Necklace': 360.0},
    'Balenciaga': {'Sneakers': 360.0, 'Cap': 180.0}
}

# TODO

Material for the VU Amsterdam course “Introduction to Python Programming” for BSc Artificial Intelligence students. These notebooks are created using the following sources:

  1. Learning Python by Doing: This book, developed by teachers of TU/e Eindhoven and VU Amsterdam, is the main source for the course materials. Code snippets or text explanations from the book may be used in the notebooks, sometimes with slight adjustments.

  2. Think Python

  3. GeekForGeeks