Loops in Python. Comprehensions.

What are loops (cycles)?

What are loops (cycles)?

Loops are control statements which allows a block of code to be executed multiple times.
This repetition of a code can be fixed number of times (for loop) or while some condition is fulfilled (while loop).
Each execution of the code, during the loop, is called an iteration!

while loop

while loop

Syntax

The while loop in Python repeatedly executes a block of code as long as a specified condition is True. It’s useful when the number of iterations is not known in advance and depends on some condition being met

                while condition :
                    block
            
The block will be executed while the condition is True!
Inside the block we have to change the variable used in condition to prevent an endless loop. Or we can use break statement (discussed further)
Flow Diagram

Simple example


            i = 1
            while i<=5 :
                print(i)
                i += 1
        

            1
            2
            3
            4
            5
        

This is just a simple example. For fixed numbers of times loops, it is better to user for with range loop, which will be discussed next

Example: endless loop (find the problem)

If you run next code, your Python will run an endless loop. Use CTRL+C or CTRL+Z to stop it


            # print the numbers from 10 to 1:
            i = 10
            while i>=1 :
                print(i)
                i = 1
        

Example: proper use case of while

While loop is suitable, when we do not know in advance the number of iterations needed

                # ask user to enter a name (string), until it contains at least 3 symbols
                # the len function on string returns the number of symbols in a string
                user_name = input("Enter a name, please: ")
                user_name_length = len(user_name)

                while user_name_length < 3:
                    user_name = input("Enter a name (at least 3 symbols): ")
                    user_name_length = len(user_name)

                print("Thank you, {}!".format(user_name))
            
Note: Repetition in the while block code is not ideal. We could refactor this by using an endless loop with while True and a break statement, which we will explore further.

for loop

for loop

Syntax

The for loop in Python is used to iterate over a sequence (like a list, tuple, dictionary, or string) and execute a block of code for each item in that sequence

                for variable in sequence:
                    # code to execute for each item
            
Example:

                for letter in "ada":
                    print(letter.capitalize())

                # A
                # D
                # A
            

Flow

Examples


            ### loop on list items:
            for item in [1,2,3]:
                print(item, end=",")     # 1,2,3,

            ### loop on tuple items:
            for item in (10, "December", 1985):
                print(item, end=",")     # 10,December,1985,

            ### loop on string items:
            for item in "byron":
                print(item, end=",")     # b,y,r,o,n,

            ### loop on range items:
            for item in range(1,3):
                print(item, end=",")     # 1,2,
        

More Examples

Iterate over range of numbers:

                for n in range(1,6):
                    print(n, end=",")               # 1,2,3,4,5,
            
Sum numbers in range (1,10):
Just an example, its better to use the built-in sum function.

                total_sum = 0
                for n in range(1,11):
                    total_sum+=n

                print(f'total_sum={total_sum}')     # total_sum=55
            
For Loop with Dictionary:

                person = {"name": "John", "age": 30, "country": "UK"}
                for key, value in person.items():
                    print(f"{key}: {value}")

                # name: John
                # age: 30
                # country: UK
            
For Loop with Tuple Unpacking

                pairs = [(1, 'one'), (2, 'two'), (3, 'three')]
                for number, name in pairs:
                    print(f"{number} is {name}")

                # 1 is one
                # 2 is two
                # 3 is three
            

Loop on indexes of sequence (C-style loops)

Sometimes we need to iterate through the indices of elements in a sequence rather than iterating through the elements themselves. In Python we can do this in two ways:
Using range(len(sequence)) to loop over the indexes of the sequence.

                colors = ["red", "green", "blue"]
                for index in range(len(colors)):
                    print(f"{index}: {colors[index]}")

                # 0: red
                # 1: green
                # 2: blue
            
Using enumerate(iterable, start=0) function that allows us to loop over a sequence and get both the index and the value of each element at the same time:

                colors = ["red", "green", "blue"]
                for index, color in enumerate(colors):
                    print(f"{index}: {color}")

                # 0: red
                # 1: green
                # 2: blue
            

Nested for loops

We can nest a for loop into another:

                for i in [1,2,3]:
                    for j in "abv":
                        print(f'{i}:{j}', end=" ")
                    print()      # prints new line

                # 1:a 1:b 1:v
                # 2:a 2:b 2:v
                # 3:a 3:b 3:v
            
And iterate over nested lists:

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

                for row in matrix:
                    for element in row:
                        print(element, end=' ')
                    print()  # new line after each row

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

break statement

break statement

Syntax

The break statement in Python (like in other C-based languages) breaks out of the innermost enclosing for or while loop.
The most common use for break is when some external condition is triggered to exit immediately from a loop.
The break statement can be used in both while and for loops.

                while condition:
                    block 1
                    if break_cond:
                        break  # loop is terminated, block 2 is skipped
                    block 2
            

                for item in sequence :
                    block 1
                    if break_cond:
                        break  # loop is terminated, block 2 is skipped
                    block 2
            

Flow

Examples:

Output letters in a string, until 'i' letter is reached:

                str = "alibaba"
                for s in str:
                    if s == "i":
                        break
                    print(s)

                # a
                # l
            
Using break to find an element in a list

                elements = ["apple", "banana", "cherry"]
                target = "banana"

                for el in elements:
                    if el == target:
                        print(f"Found {target}")
                        break

                # Found banana
            

Example: Using break in nested loops


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

            search_value = 4

            for row in matrix:
                print(f'Processing row: {row}')
                for element in row:
                    print(element, end=',')
                    if element == search_value:
                        break  # Breaks out of the inner loop
                print() # print new line

            # Processing row: [1, 2, 3]
            # 1,2,3,
            # Processing row: [4, 5, 6]
            # 4,
            # Processing row: [7, 8, 9]
            # 7,8,9,
        

do-while emulation with break

Python did not have do-while loop, as in other languages (reason: "There should be one - and preferably only one - obvious way to do it.", The Zen of Python)
But do-while structure is useful when we need the body of the while loop to be executed at least one.
General structure of a "do-while" loop, which did not exists in Python:

                do {
                    # will be executed at least one
                    block
                } while (condition);
            
But it can be easily emulated if needed

                while True:
                    block

                    if (condition): break
            

Example - do-while emulation with break

Ask user to enter a name, until it contains at least 3 symbols

                while True:
                    user_name = input("Enter a name (at least 3 symbols): ")
                    user_name_length = len(user_name)

                    if user_name_length > 3: break

                print(f"Thank you, {user_name}!")

                # Enter a name (at least 3 symbols): iv
                # Enter a name (at least 3 symbols): a
                # Enter a name (at least 3 symbols): ivan
                # Thank you, ivan!
            

"switch-case" statement emulation for Python<3.10

In oldest python versions you can emulate ""switch-case"" with multiple if-elif-break blocks

                # print program menu:
                print("Select an action:")
                print("1. Action 1")
                print("2. Action 3")
                print("3. Action 3")
                print()

                while True:
                    user_choice = int(input("Enter a number [1-3]: "))

                    if user_choice == 1:
                        print("Action 1 fired!")
                        break
                    elif user_choice == 2:
                        print("Action 2 fired!")
                        break
                    elif user_choice == 3:
                        print("Action 3 fired!")
                        break
                    else:
                        print('Wrong input!')

                # Select an action:
                # 1. Action 1
                # 2. Action 3
                # 3. Action 3

                # Enter a number [1-3]: 7
                # Wrong input!
                # Enter a number [1-3]: 2
                # Action 2 fired!
            

match statement

Python 3.10 introduces match statement is superficially similar to a switch statement in C, Java or JavaScrip.
match takes an expression and compares its value to successive patterns given as one or more case blocks.

                # print program menu:
                print("Select an action:")
                print("1. Action 1")
                print("2. Action 3")
                print("3. Action 3")
                print()

                while True:
                    user_choice = int(input("Enter a number [1-3]: "))

                    match user_choice:
                        case 1:
                            print("Action 1 fired!")
                        case 2:
                            print("Action 2 fired!")
                        case 3:
                            print("Action 3 fired!")
                        case _
                            # default case that matches anything not handled by previous cases.
                            print('Wrong input!')

                # Select an action:
                # 1. Action 1
                # 2. Action 3
                # 3. Action 3

                # Enter a number [1-3]: 7
                # Wrong input!
                # Enter a number [1-3]: 2
                # Action 2 fired!
            

continue statement

continue statement

Returns the control to the beginning of the loop.
code after continue will be skipped.
Usually, continue statement is dependent on some condition.

Syntax


            while condition:
                block 1

                if continue_cond:
                    continue  # go to while condition

                block 2
        

            for item in sequence :
                block 1

                if continue_cond:
                    continue  # continue loop with next item

                block 2
        

Examples:

print all numbers in [1..5], but skip 3:

                for i in range(1,6):
                if i == 3:
                    continue
                print(i, end=',')  # 1,2,4,5,
            
print letters in a word, excluding vowels:

                word = 'abcdefghi'
                vowels = ['a', 'e', 'i', 'o', 'u']

                for l in word:
                    if l in vowels:
                        continue
                    print(l, end=",")

                # b,c,d,f,g,h,
            

Comprehensions

List Comprehensions

Overview

List comprehensions in Python are a concise way to create lists based on existing sequences. They provide a more elegant and readable alternative to traditional for loops when you need to create a new list by transforming or filtering elements.

                new_list = [expression for item in iterable if condition]
            
where:
expression is the item to be returned and added to the new_list. I.e. the transformation to apply to each item.
iterable is the sequence you're iterating over (e.g., list, range, string, etc.)
item is the variable representing each element in the iterable.
if condition is optional filter to include only elements that match a condition
Examples:

                # Map letters to its uppercase
                letters = ['a', 'b', 'c']
                upper_letters = [l.upper() for l in letters]
                print(upper_letters) #['A', 'B', 'C']

                # Filtering Even Numbers:
                numbers = [1,2,3,4,5]
                evens = [el for el in numbers if el%2==0]
                print(evens) # [2, 4]
            

For loop vs List Comprehensions

Example 1: Creating a list of squares from another iterable
with for loop

                squares = []
                for x in range(5):
                    squares.append(x**2)

                print(squares)
                #[0, 1, 4, 9, 16]
            
with list comprehension

                squares = [x**2 for x in range(5)]

                print(squares)
                #[0, 1, 4, 9, 16]
            

For loop vs List Comprehensions

Example 2: Filtering elements
with for loop

                even_numbers = []
                for x in range(10):
                    if x % 2 == 0:
                        even_numbers.append(x)

                        print(even_numbers)
                # [0, 2, 4, 6, 8]
            
with list comprehension

                even_numbers = [x for x in range(10) if x%2==0]

                print(even_numbers)
                # [0, 2, 4, 6, 8]
            

More Examples


            # Map letters to its uppercase
            letters = ['a', 'b', 'c']
            upper_letters = [l.upper() for l in letters]
            print(upper_letters)
            #['A', 'B', 'C']

            # Map Celsius temperatures to Fahrenheit
            celsius = [0, 20, 37, 100]
            fahrenheit = [((temp * 9/5) + 32) for temp in celsius]
            print(fahrenheit)
            #[32.0, 68.0, 98.6, 212.0]

            # Filtering Even Numbers:
            numbers = [1,2,3,4,5]
            evens = [num for num in numbers if num % 2 == 0]
            print(evens)
            #[2, 4]

            # Filtering email addresses
            emails = ["user1@gmail.com", "user2@yahoo.com", "user3@gmail.com"]
            gmail_users = [email for email in emails if "@gmail.com" in email]
            print(gmail_users)
            #['user1@gmail.com', 'user3@gmail.com']

            # Flattening a nested list
            matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
            flat_list = [num for row in matrix for num in row]
            print(flat_list)
            #[1, 2, 3, 4, 5, 6, 7, 8, 9]
        

Dictionary Comprehensions

Dictionary comprehensions in Python are similar to list comprehensions but are used to create dictionaries.
A dictionary comprehension consists of an expression pair (key: value) followed by a for statement inside curly braces {}.

                new_dict = {key: value for item in iterable if condition}
            
where:
key is the expression that defines the key in each key-value pair.
value is the expression that defines the value in each key-value pair.
item, iterable and condition are the same like in list comprehensions.
Example:

                eng_bul = {
                    'apple':'ябълка',
                    'orange':'портокал',
                    'banana':'банан'
                }

                # swapping keys and values
                bul_eng = {v:k for k,v in eng_bul.items()}
                print(bul_eng)

                # {'ябълка': 'apple', 'портокал': 'orange', 'банан': 'banana'}
            

Examples

Create new dictionary from student_scores, containing only the items which has values > 4.0

                student_scores = {
                    'ivan':4.5,
                    'maria':5.0,
                    'asen':3.5
                }

                best_students = {k:v for k,v in student_scores.items() if v>4}

                print(best_students)

                # {'ivan': 4.5, 'maria': 5.0}
            
Creating a dictionary of even numbers and their squares:

                even_squares = {x: x**2 for x in range(10) if x % 2 == 0}
                print(even_squares)

                # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
            
Creating a Dictionary from Two Lists

                keys = ['a', 'b', 'c']
                values = [1, 2, 3]
                dictionary = {keys[i]: values[i] for i in range(len(keys))}
                print(dictionary)

                # {'a': 1, 'b': 2, 'c': 3}
            

Example: Frequency Counter for Text Analysis


            #Example: Counting word frequency in a text
            text = "apple banana apple apple banana plum"
            words = text.split()

            word_frequency = {word: words.count(word) for word in set(words)}

            print(word_frequency)
            # {'apple': 3, 'banana': 2, 'plum': 1}
        

Example: Data Filtering for Reports


            # Filtering sales data for a quarterly report
            sales_data = {
                'Q1': {'Jan': 10000, 'Feb': 12000, 'Mar': 15000},
                'Q2': {'Apr': 14000, 'May': 16000, 'Jun': 18000},
                'Q3': {'Jul': 17000, 'Aug': 19000, 'Sep': 21000},
                'Q4': {'Oct': 20000, 'Nov': 22000, 'Dec': 25000}
            }
            # Get quarters with average sales above 18000
            high_performing_quarters = {q: sum(months.values())/len(months)
                                        for q, months in sales_data.items()
                                        if sum(months.values())/len(months) > 18000}
            pprint(high_performing_quarters, indent=4)
            # {'Q3': 19000.0, 'Q4': 22333.333333333332}
        

Exercises

HW

Tasks on Loops and Data Strucures

The tasks are given in next gist file
You can copy it and work directly on it. Just put your code under "### Your code here".

"Guess the number" game

Task: Create a Python program for a "Guess the Number" game. The game will generate a random number within a specified range and prompt the user to guess the number. The program provides feedback based on the user's guess - whether it's too high, too low, or correct. The game continues until the user guesses the number correctly.
Requirements:
Random Number Generation: The program should generate a random number within a given range (e.g., 1 to 10). You can use the random.randint(1, 10) method, after including in your code import random
User Input: Prompt the user to enter their guess. Ensure, the user guess is in the range, and if not, display "Your guess is out of bounds."
Feedback to User: After each guess, the program should provide feedback:
  • If the guess is too high, display "Too hight!"
  • If the guess is too low, display "Too low!"
  • If the guess is correct, display "Congratulations! You guessed it right."
Repeat Guesses: The game should continue, allowing the user to guess again if the guess is incorrect.
Terminate the Game: Once the correct number is guessed, the game should end with message "Bravo, you guessed my number"
Optional - Guess Counter: Keep track of the number of guesses the user makes and display this count when the correct number is guessed.
Bonus Challenge:
Add a feature to limit the number of guesses a user can make. If the user doesn't guess the number within the limit, end the game with a message indicating that the user has lost.
Implement a difficulty level for the game (e.g., easy, medium, hard), where each level has a different range of numbers or a different number of allowed guesses.

"Guess the number" - demo output


            Welcome to Guess the Number!

            ********************* Levels *********************
            * 1. EASY, range: (1, 10), max moves: 7          *
            * 2. MEDIUM, range: (1, 10), max moves: 5        *
            * 3. HARD, range: (1, 100), max moves: 10        *
            **************************************************

            >>> Select level: 5
                ( Enter a number between 1 and 3 )

            >>> Select level: 2

            *** Let's play MEDIUM. Good Luck! ***

            >>> Guess the number (between 1 and 10): 5
            Too high! Moves left: 4

            >>> Guess the number (between 1 and 10): 1
            Too low! Moves left: 3

            >>> Guess the number (between 1 and 10): 2

            *** Congratulations! You guessed it right in 3 moves!. ***
        

These slides are based on

customised version of

Hakimel's reveal.js

framework