2.3. Loops#

Let’s do another step to automatize things even more! Previous Sections introduced a lot of fundamental concepts, but they still don’t unveil the true power of any programming language — loops!

If we want to perform the same procedure multiple times, then we would have to take the same code and copy-paste it. This approach would work, however it would require a lot of manual work and it does not look cool.

This problem is resolved with a loop construction. As the name suggest, this construction allows you to loop (or run) certain piece of code several times at one execution.

2.3.1. for loop#

The first and the most popular looping technique is a for loop. Let’s see some examples:

Let’s create a list with some stuff in it. In order to iterate (or go through each element of a list) we use a for loop.

my_list = [100, 'marble', False, 2, 2, [7, 7, 7], 'end']

print('Start of the loop')
for list_item in my_list:
    print('In my list I can find:', list_item)
print('End of the loop')
Start of the loop
In my list I can find: 100
In my list I can find: marble
In my list I can find: False
In my list I can find: 2
In my list I can find: 2
In my list I can find: [7, 7, 7]
In my list I can find: end
End of the loop

General for loop construction looks like this:

for iterator_variable in iterable: do something with iterator_variable

During each iteration the following steps are happening under the hood (or above it):

  1. iterator_variable = iterable[0]iterator_variable is assigned the first value from the iterable.

  2. Then, you use iterator_variable as you wish.

  3. By the end of the ‘cycle’, the next element from the iterable is selected (iterable[1]), i.e., we return to step 1, but now assigning the second element… and so on.

  4. When there is not a next element (in other words, we have reached the end of the iterable) — it exits and the code under the loop is now executed.

Looks cool, but what if we want to alter the original iterable (not the iterator_variable) within the loop?

x = my_list
print('Try #1, before:', x)

for item in x:
    item = [5,6,7]

print('Try #1, after', x)
Try #1, before: [100, 'marble', False, 2, 2, [7, 7, 7], 'end']
Try #1, after [100, 'marble', False, 2, 2, [7, 7, 7], 'end']

Nothing has changed…. let’s try another method. range() is used to generate a sequence of numbers more info here.

range(length_of_x) will generate numbers from 0 till length_of_x, excluding the last one.

length_of_x = len(x)

indices = range(length_of_x)

print(indices)
print('Try #2, before', my_list)

for id in indices:
    my_list[id] = -1

print('Try #2, after', my_list)
 
range(0, 7)
Try #2, before [100, 'marble', False, 2, 2, [7, 7, 7], 'end']
Try #2, after [-1, -1, -1, -1, -1, -1, -1]

Now we have a method in our arsenal which can not only loop through a list but also access and alter its contents. Also, you can generate new data by using a for loop and by applying some processing to it. Here’s an example on how you can automatize your greetings routine!

We create a variable message with a general greeting and a list with your friends names. Then an empty list where all greetings will be stored (otherwise you cannot use the .append in the for loop below!).

message = "Ohayo"
names = ["Mike", "Alex", "Maria"]
greetings = []

for name in names:
    personalized_greeting = f'{message}, {name}-kun!' 
    greetings.append(personalized_greeting) 

print(greetings)
['Ohayo, Mike-kun!', 'Ohayo, Alex-kun!', 'Ohayo, Maria-kun!']

And you can also have loops inside loops!. Let’s say that you put down all your expenses per day separately in euros. You can also keep them within one list together.Additionally, you can access also each expense separately! day3 is third array and 2nd expense is second element within that array.

day1_expenses = [15, 100, 9]
day2_expenses = [200]
day3_expenses = [10, 12, 15, 5, 1]

expenses = [day1_expenses, day2_expenses, day3_expenses]
print('All my expenses', expenses)

print(f'My second expense on day 3 is {expenses[2][1]}')
All my expenses [[15, 100, 9], [200], [10, 12, 15, 5, 1]]
My second expense on day 3 is 12

Now let’s use it in some calculations. The code bellow iterates over the expenses for each day, calculates the total expenses for each day, and then adds them together to obtain the overall total expenses.

total_expenses = 0

for i in range(len(expenses)): 
    daily_expenses_list = expenses[i]
    daily_expenses = 0
    for j in range(len(daily_expenses_list)): 
        daily_expenses += daily_expenses_list[j]
    total_expenses += daily_expenses
    
print(f'Option #1: In total I have spent {total_expenses} euro!')
Option #1: In total I have spent 367 euro!

Let’s break it down

This code calculates the total expenses over multiple days using nested loops. Here’s an explanation in simpler terms:

  1. We start with the variable total_expenses set to 0 to keep track of the total expenses.

  2. The code loops over each day’s expenses using the outer loop, which runs from 0 to the length of the expenses list.

  3. Inside the loop, it accesses the expenses made on the current day by assigning daily_expenses_list to the expenses at index i.

  4. It initializes daily_expenses as 0 to temporarily store the sum of expenses for the current day.

  5. The code enters the inner loop, which iterates over the expenses for the current day using the range of the length of daily_expenses_list.

  6. Inside the inner loop, it adds each expense to daily_expenses to calculate the total expenses for the current day.

  7. After the inner loop completes, it adds daily_expenses to the total_expenses variable to accumulate the expenses across all days.

  8. Once the outer loop finishes, it prints the total expenses using an f-string format to display the result.

Option #2

total_expenses = 0

for i in range(len(expenses)):
    for j in range(len(expenses[i])):
        total_expenses += expenses[i][j]
    
print(f'Option #2: In total I have spent {total_expenses} euro!')
Option #2: In total I have spent 367 euro!

Option #3 - advanced techniques gathered after eternal suffering.

total_expenses = 0
total_expenses = sum(map(sum, expenses))
print(f'Option #3: In total I have spent {total_expenses} euro!')
Option #3: In total I have spent 367 euro!

2.3.2. while loop#

The second popular loop construction is a while loop. The main difference is that it is suited for code structures that must repeat unless a certain logical condition is satisfied. It looks like this:

while logical_condition == True:
do something

And here is a working code example:

sum = 0

while sum < 5:
    print('sum in the beginning of the cycle:', sum)
    sum += 1
    print('sum in the end of the cycle:', sum)
sum in the beginning of the cycle: 0
sum in the end of the cycle: 1
sum in the beginning of the cycle: 1
sum in the end of the cycle: 2
sum in the beginning of the cycle: 2
sum in the end of the cycle: 3
sum in the beginning of the cycle: 3
sum in the end of the cycle: 4
sum in the beginning of the cycle: 4
sum in the end of the cycle: 5

As you can see, this loop was used to increase the value of the sum variable until it reached \(5\). The moment it reached \(5\) and the loop condition was checked — it returned False and, therefore, the loop stopped.

Additionally, it is worth to mention that the code inside the loop was altering the variable used in the loop condition statement, which allowed it to first run, and then stop. In the case where the code doesn’t alter the loop condition, it won’t stop (infinite loop), unless another special word is used.

Here’s a simple example of an infinite loop, which you may run (by removing the #’s) but in order to stop it — you have to interrupt the Notebook’s kernel or restart it.

# a, b = 0, 7

# while a + b < 10:
#     a += 1
#     b -= 1
#     print(f'a:{a};b:{b}')

2.3.3. break keyword#

After meeting and understanding the loop constructions, we can add a bit more control to it. For example, it would be nice to exit a loop earlier than it ends — in order to avoid infinite loops or just in case there is no need to run the loop further. This can be achieved by using the break keyword. The moment this keyword is executed, the code exits from the current loop.

stop_iteration = 4

print('Before normal loop')
for i in range(7):
    print(f'{i} iteration and still running...')
print('After normal loop')

print('Before interrupted loop')
for i in range(7):
    print(f'{i} iteration and still running...')

    if i == stop_iteration:
        print('Leaving the loop')
        break
print('After interupted loop')
Before normal loop
0 iteration and still running...
1 iteration and still running...
2 iteration and still running...
3 iteration and still running...
4 iteration and still running...
5 iteration and still running...
6 iteration and still running...
After normal loop
Before interrupted loop
0 iteration and still running...
1 iteration and still running...
2 iteration and still running...
3 iteration and still running...
4 iteration and still running...
Leaving the loop
After interupted loop

The second loop shows how a small intrusion of an if statement and the break keyword can help us with stopping the loop earlier. The same word can be also used in a while loop:

iteration_number = 0

print('Before the loop')
while True:
    iteration_number += 1

    print(f'Inside the loop #{iteration_number}')
    if iteration_number > 5:
        print('Too many iterations is bad for your health')
        break
print('After the loop')
Before the loop
Inside the loop #1
Inside the loop #2
Inside the loop #3
Inside the loop #4
Inside the loop #5
Inside the loop #6
Too many iterations is bad for your health
After the loop

2.3.4. continue keyword#

Another possibility to be more flexible when using loops is to use the continue keyword.

This will allow you to skip some iterations (more precisely — the moment the keyword is used it will skip the code underneath it and will start the next iteration from the beginning).

def calculate_cool_function(arg):
    res = 7 * arg ** 2 + 5 * arg + 3
    print(f'Calculating cool function for {arg} ->  f({arg}) = {res}')

print('Begin normal loop\n')
for i in range(7):
    print(f'{i} iteration and still running...')
    calculate_cool_function(i)
print('\nEnd normal loop\n')

print('-------------------')

print('Begin altered loop\n')
for i in range(7):
    print(f'{i} iteration and still running...')

    # skipping every even iteration
    if i % 2 == 0:
        continue
        
    calculate_cool_function(i)
    
print('\nEnd altered loop')
Begin normal loop

0 iteration and still running...
Calculating cool function for 0 ->  f(0) = 3
1 iteration and still running...
Calculating cool function for 1 ->  f(1) = 15
2 iteration and still running...
Calculating cool function for 2 ->  f(2) = 41
3 iteration and still running...
Calculating cool function for 3 ->  f(3) = 81
4 iteration and still running...
Calculating cool function for 4 ->  f(4) = 135
5 iteration and still running...
Calculating cool function for 5 ->  f(5) = 203
6 iteration and still running...
Calculating cool function for 6 ->  f(6) = 285

End normal loop

-------------------
Begin altered loop

0 iteration and still running...
1 iteration and still running...
Calculating cool function for 1 ->  f(1) = 15
2 iteration and still running...
3 iteration and still running...
Calculating cool function for 3 ->  f(3) = 81
4 iteration and still running...
5 iteration and still running...
Calculating cool function for 5 ->  f(5) = 203
6 iteration and still running...

End altered loop

As you can see, with the help of the continue keyword we managed to skip some of the iterations. Also worth noting that \(0\) is divisible by any number, for that reason the calculate_cool_function(i) at i = 0 didn’t run.

2.3.5. Additional study material#

2.3.5.1. After this Notebook you should be able to:#

  • generate an array for the x-axis

  • calculate the cos or sin of the x-axis

  • plot such functions

  • understand how to load Python files as modules

  • understand conditions with if, elif and, else

  • understand the differences between list, tuple, and dict

  • slice lists and tuples

  • use for and while loops

  • use the break and continue keywords inside of loops