Loops
for loops
The first control flow statement we’ll look at is called a for loop.
Run the following code and see what happens
example_list = ['a', 'b', 'c', 'd', 'e']
for each_lettter in example_list:
print(each_lettter)You should see that the elements of the list are printed, one by one.
The above for loop loops over every element of the list and does something for each one - in our case it prints a value the screen.
Iterations
As we loop over the elements of an interable, it might be useful to keep a track of how many times the loop has run. We call this the number of iterations. The simplest way would be to use a variable that keeps track of the iterations
some_numbers = (5, 10, 15, 20, 25)
iteration_count = 1
for number in some_numbers:
print(f'This is iteration #{iteration_count}')
print(number)
iteration_count = iteration_count + 1This is iteration #1
5
This is iteration #2
10
This is iteration #3
15
This is iteration #4
20
This is iteration #5
25
We start with a variable iteration_count=1, and every time the loop runs this variable is printed, and then its value is increased or incremented by 1. This means that iteration_count then tracks which iteration the loop is currently on.
This works, but it’s messy. Luckily, Python gives us a built-in function which does this for us - enumerate.
some_numbers = (5, 10, 15, 20, 25)
for iteration_count, number in enumerate(some_numbers):
print(f'This is iteration #{iteration_count:d}')
print(number)This is iteration #0
5
This is iteration #1
10
This is iteration #2
15
This is iteration #3
20
This is iteration #4
25
This gives us almost exactly the same result as before, but with one key difference - here the iterations are counted starting from zero, just like when indexing a list or tuple.
The enumerate function pairs each value of a given iterable with an index and provides a way to keep track of the iteration count.
Ranges
Sometimes we want to loop over a simple sequence of integers. We could do this, as in our previous examples, like so
integers = [0, 1, 2, 3, 4]
for integer in integers:
print(integer)0
1
2
3
4
On the other hand, as is probably becoming familiar to you at this point, Python provides us with a shortcut via a built-in function. This time, we are interested in the range function
for integer in range(0, 5):
print(integer)0
1
2
3
4
Which gives us exactly the same result, with one less line of code. The range function takes two arguments, an integer from which to start the sequence, and an integer at which to terminate the sequence. Note that, much like slicing lists, the latter is not included in the final sequence (here we supply 5 as the second argument, but the sequence terminates at 4).
We can actually get away with shortening our code slightly more
for integer in range(5):
print(integer)0
1
2
3
4
If the range function is only given one input, then Python assumes that you want the sequence to start from zero (again much like slicing lists). On the other end of the spectrum, we can also provide three arguments to range
for integer in range(0, 5, 2):
print(integer)0
2
4
This third argument, once more just like list slicing, acts as a step size. Here we set it to 2, so we get only the even-numbered integers. We can use a negative step size to loop through numbers in reverse order (from bigger to smaller)
for integer in range(5, 0, -1):
print(integer)5
4
3
2
1
Multiple iterables
How might we write a for loop to calculate the element-wise product of the following two lists?
evens = [1, 3, 5, 7, 9]
odds = [0, 2, 4, 6, 8]Python provides us with a function that takes in two or more iterables and returns the corresponding number of values in each iteration of a loop. This function is called zip, and can be used to solve the above problem.
for even, odd in zip(evens, odds):
print(even * odd)0
6
20
42
72
Nested loops
Just as we saw that we can have nested lists (e.g. a list in a list) we can also have nested loops.
Run the following code and see what happens
list_1 = [1, 2, 3]
list_2 = ['a', 'b', 'c']
for item_i in list_1:
for item_j in list_2:
print(item_i, item_j)To understand what’s going on inside of these loops, let’s use enumerate and an additional print statement.
list_1 = [1, 2, 3]
list_2 = ['a', 'b', 'c']
for iteration_count, item_i in enumerate(list_1):
print(f'Outer loop iteration #{iteration_count}')
for item_j in list_2:
print(item_i, item_j)Outer loop iteration #0
1 a
1 b
1 c
Outer loop iteration #1
2 a
2 b
2 c
Outer loop iteration #2
3 a
3 b
3 c
We’re now printing every time there is an iteration of the outer loop.
So the outer loop iterates three times, and inside each three lines of data are printed. Let’s add another enumerate function call and print statement, but this time to the inner loop.
list_1 = [1, 2, 3]
list_2 = ['a', 'b', 'c']
for iteration_count_i, item_i in enumerate(list_1):
print(f'Outer loop iteration #{iteration_count_i}')
for iteration_count_j, item_j in enumerate(list_2):
print(f'Inner loop iteration #{iteration_count_j}')
print(item_i, item_j)Outer loop iteration #0
Inner loop iteration #0
1 a
Inner loop iteration #1
1 b
Inner loop iteration #2
1 c
Outer loop iteration #1
Inner loop iteration #0
2 a
Inner loop iteration #1
2 b
Inner loop iteration #2
2 c
Outer loop iteration #2
Inner loop iteration #0
3 a
Inner loop iteration #1
3 b
Inner loop iteration #2
3 c
This might look a little bit confusing, but breaking it down step by step we see that
- The outer loop starts, printing its iteration number and values from
list_1. - Then the inner loop runs three times, each time printing its iteration number and values from
list_2. - The outer loop iterates again, and so on.
For every iteration of the outer loop, the inner loop iterates through the entirety of list_2. So the inner loop does all of its iterations before the outer loop iterates again.
Exercises
1a. Using a for loop, generate and print the first 20 square numbers.
You can generate integers from 1 to 20 with the range function.
1b. Modify your loop to keep track of the current iteration and print this along with each square number.
2. The Fibonacci sequence is defined by the recursion relation
\[F_{n} = F_{n - 1} + F_{n - 2},\]
i.e. each term is simply the sum of the previous two terms.
Starting with the following list:
fib = [0, 1]2a. Using list indexing, calculate the next term in the sequence and append this to fib.
Remember that you can use negative indices to access elements in a list in reverse order.
2b. Write a loop to calculate the next 10 terms in the sequence and add these to fib.