**Table of Contents**

## Introduction

We know that Python statements are executed sequentially. That is, the first statement in a program is executed first, followed by the second and so on. There may be a situation when you need to execute a block of code several times. A **loop** statement allows us to execute a code block multiple times without repeating the code in the program.

## The for Loop

The `for`

loop in Python is used to iterate over iterable objects like list, tuple, string, sets and dictionaries. The syntax of the `for`

loop is as follows:

Syntax

**The**.

`for`

loop```
1for item in sequence:
2 loop body
```

The variable `item`

is called the **loop variable**. It changes for each iteration of the loop. The loop continues until we reach the last item in the sequence. Notice that the body of for loop is indented with a tab.

Example

The`for`

loop.
```
1list1 = [2,4,6,8,10]
2for item in list1:
3 print(item)
```

```
2
4
6
8
10
```

In the following example, we compute the sum of the squares of all values in the list. Note that we need to **initialize the sum to zero** before we begin the loop.

Example

Compute a sum using the`for`

loop.
```
1sum_squares = 0
2for item in list1:
3 sum_squares = sum_squares + item**2
4print(f"The sum of the squares is {sum_squares}")
```

```
The sum of the squares is 220
```

Mathematically, we are computing the sum:
$$ \sum_{x \in \mathcal{B}} x^2 = 2^2 + 4^2+6^2+8^2+10^2$$
where $\mathcal{B}$ is the set of all elements in `list1`

.

Recall that only the indented statement is within the for loop. Therefore, the last print statement is not subjected to any iteration. The following shows the inner workings of the for loop for each iteration. The `temp`

variable is created to store the sum before it is being updated (for the purpose of printing).

Example

Dissecting the`for`

loop to compute a sum.
```
1sum_squares = 0
2for item in list1:
3 temp = sum_squares
4 sum_squares = sum_squares + item**2
5 print(f"{item}: the sum is now {temp} + {item**2} = {sum_squares}")
6print(f"The sum of the squares is {sum_squares}")
```

```
2: the sum is now 0 + 4 = 4
4: the sum is now 4 + 16 = 20
6: the sum is now 20 + 36 = 56
8: the sum is now 56 + 64 = 120
10: the sum is now 120 + 100 = 220
The sum of the squares is 220
```

It is possible to loop over the elements in a Python set (which we may recall is both unordered and unindexed). However, the result may not follow the order in which the items appear in the set.

Example

Looping through a set.```
1set1 = {2,4,6,8}
2for item in set1:
3 print(item)
```

```
8
2
4
6
```

We can also loop over dictionaries. By default, Python will **loop over the keys only**.

Example

Looping through a dictionary.```
1scores = {"Lily":62, "Jane":86, "Tom":75, "Matt":91}
2for item in scores:
3 print(item)
```

```
Lily
Jane
Tom
Matt
```

To **loop over the values**, we use the `values()`

method.

Example

Looping through the values of a dictionary.```
1for item in scores.values():
2 print(item)
```

```
62
86
75
91
```

To loop over the `key:value`

pairs as tuples, we use the `items()`

method.

Example

Looping through the`key:value`

pairs of a dictionary.
```
1for item in scores.items():
2 print(item)
```

```
('Lily', 62)
('Jane', 86)
('Tom', 75)
('Matt', 91)
```

We may also separate the keys and the values by creating two loop variables in a tuple.

Example

Looping through a dictionary: separating keys and values.```
1for (name,score) in scores.items():
2 print(f'{name} receives a score of {score}')
```

```
Lily receives a score of 62
Jane receives a score of 86
Tom receives a score of 75
Matt receives a score of 91
```

ADVERTISEMENT

## Some Useful Functions

In this section, we introduce several useful functions related to looping.

### The range() Function

We can generate a sequence of integers using the `range()`

function. For example, `range(5)`

will generate numbers from 0 to 4 (5 numbers). The syntax is as follows:

Syntax

**The**.

`range()`

function```
1 range(start, stop, step)
```

Parameter | Required? | Default Value | Description |
---|---|---|---|

`start` |
❌ No | 0 | An integer number specifying the start index of the range object. |

`stop` |
✔️ Yes | NA | An integer number specifying the stop index of the range object. |

`step` |
❌ No | 1 | An integer number specifying the step size. |

The `stop`

argument is mandatory. Both the `start`

and `step`

arguments are optional. The start argument defaults to `0`

and `step`

argument defaults to `1`

if not provided. Note that the `range`

object produces integers **up to but not including the endpoint**. The range object evaluates “lazily” in the sense that you will not see the sequence generated unless you explicity convert it to a sequence using the `list()`

or `tuple()`

constructors for example.

Example

The`range()`

function.
```
1range(5) # sequence is hidden
```

```
range(0, 5)
```

Example

Converting a`range()`

object to a list.
```
1list(range(5)) # convert to list object.
```

```
[0, 1, 2, 3, 4]
```

Example

Converting a`range()`

object to a tuple.
```
1tuple(range(5)) # converts to tuple object.
```

```
(0, 1, 2, 3, 4)
```

Example

The`range()`

function with `start`

and `stop`

arguments.
```
1list(range(2,8)) # range object with start and stop arguments.
```

```
[2, 3, 4, 5, 6, 7]
```

Example

The`range()`

function with all 3 arguments.
```
1list(range(3,20,2)) # range object with start, stop and step_size arguments.
```

```
[3, 5, 7, 9, 11, 13, 15, 17, 19]
```

We may use the `reversed()`

function on a `range()`

object to create the sequence in reversed order.

Example

The`reversed()`

function applied to a `range()`

object.
```
1list(reversed(range(1,10, 2)))
```

```
[9, 7, 5, 3, 1]
```

Let’s see how the `range`

object is being used in a `for`

loop. Let’s compute the sum of the even numbers from $2$ to $100$, i.e. $2+4+6+…+100$.

In mathematical notation, let $\mathcal{S} = \{ x | x \in \mathbb{Z}, 1\leq x \leq 50 \}$ where $\mathbb{Z}$ is the set of all integers. We then compute the sum:

$$ \sum_{i \in \mathcal{S}} 2i = 2 + 4+ 6 + \cdots + 96+98 + 100. $$

Example

Looping through a`range()`

object.
```
1total = 0
2for i in range(101):
3 if i % 2 == 0: # modulo operator to ensure that only even numbers get summed
4 total += i
5print(f'Sum of all even numbers from 1 to 100 is {total}')
```

```
Sum of all even numbers from 1 to 100 is 2550
```

In the above code, `total += i`

is a shorter way of writing `total = total + i`

. Also, the `%`

symbol is the modulo character that has been discussed here
. For example, `i % 2`

computes the remainder when `i`

is divided by `2`

. Consequently, a remainder of `0`

means the number `i`

is divisible by `2`

and is even.

**Tip**

There are more efficient methods to compute the sum. One way is to create a range object that contains only the numbers in the series and then apply the `sum()`

function.

Example

Using`sum()`

to compute the sum of a sequence of even numbers.
```
1numbers = list(range(2,101,2))
2print(f'Sum of all even numbers from 1 to 100 is {sum(numbers)}')
```

```
Sum of all even numbers from 1 to 100 is 2550
```

### The enumerate() Function

If you need a **counter variable** while looping over a sequence, you may use the `enumerate()`

function. It returns a sequence of `(index, element)`

tuples. By default, the index starts at zero with a step size of one. Alternatively, you can specify an optional start index. The syntax is as follows:

Syntax

**The**.

`enumerate()`

function```
1 enumerate(sequence, start)
```

Parameter | Required? | Default Value | Description |
---|---|---|---|

`sequence` |
✔️ Yes | NA | An iterable object that supports iteration. |

`start` |
❌ No | 0 | The index value from which the counter is to be started. |

The `sequence`

argument is mandatory while the `start`

argument is optional. The start argument defaults to `0`

if not specified.

Example

Using`enumerate()`

function in a `for`

loop.
```
1fruits= ["apple","orange","banana","pear","pineapple","durian"]
2for item in enumerate(fruits):
3 print(item)
```

```
(0, 'apple')
(1, 'orange')
(2, 'banana')
(3, 'pear')
(4, 'pineapple')
(5, 'durian')
```

If you want to “decouple” the `(index, element)`

tuple, you can create two loop variables in a tuple.

Example

Decoupling the`(index, element)`

tuple in `enumerate()`

.
```
1for (index, fruit) in enumerate(fruits):
2 print(f'{index}: {fruit}')
```

```
0: apple
1: orange
2: banana
3: pear
4: pineapple
5: durian
```

We can specify an optional `start`

index in the enumerate function.

Example

Specifying the`start`

index in `enumerate()`

.
```
1for (index, fruit) in enumerate(fruits,2):
2 print(f'{index}: {fruit}')
```

```
2: apple
3: orange
4: banana
5: pear
6: pineapple
7: durian
```

### The random.sample() Function

Since we have discussed the `range()`

function, we will introduce a closely related function called the `random.sample()`

function which is part of Python’s **random module** used to work with random data generation. First, we need to import the module using a import random statement.

Syntax

**Importing the**.

`random`

module```
1import random
```

We use `random.sample(range(), n)`

to generate a list of `n`

random integers within the specified `range()`

. Take note that **no repetition is allowed**. Therefore, the number `n`

must not be more than the number of integers within the specified range. Essentially, the `sample()`

function takes the items from the specified range object and mixes them randomly.

Example

The`random.sample(range(), n)`

function.
```
1random.sample(range(1,11),10) # 10 random integers with range(1,11).
```

```
[8, 10, 9, 2, 3, 1, 7, 6, 5, 4]
```

In the above code, we are generating 10 random integers within the `range(1,11)`

.

**Caution**

If we had used any value of $n>10$, we will get an error since there are only 10 integers within the specified range and the random integers generated **cannot be repeated**.

We can get the list sorted using the `sorted()`

function.

Example

The`sorted()`

function to sort a list.
```
1sorted(random.sample(range(1,11),10))
```

```
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```

Example

The`sorted()`

function to sort a list (in reverse order).
```
1sorted(random.sample(range(1,11),10), reverse=True)
```

```
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
```

What if you want repetitions of the numbers from the range object? That brings us to the `random.choices()`

function.

### The random.choices() Function

We use the `random.choices(range(), k=n)`

function to select `k=n`

multiple random items from a `range()`

object (or any iterable sequence) with **repetitions allowed**.

Example

The`random.choices(range(), k=n)`

function.
```
1random.choices(range(1,11),k=10)
```

```
[5, 8, 10, 1, 1, 1, 8, 2, 7, 10]
```

Since repetitions are allowed, the is no limit on the value of `k`

.

```
1random.choices(range(1,11),k=15)
```

```
[1, 4, 8, 5, 4, 7, 1, 5, 2, 6, 9, 7, 2, 8, 9]
```

Let’s see how we can use the above random functions with `for`

loops.

Example

Use a random number generator to simulate the tossing of an unbiased die 10 times and print out the results.```
1tosses = random.choices(range(1,7),k=10) # generate a list of random integers between 1 and 6, 10 times.
2for (toss,item) in enumerate(tosses,1):
3 print(f'Toss {toss} shows {item}')
```

```
Toss 1 shows 1
Toss 2 shows 1
Toss 3 shows 4
Toss 4 shows 4
Toss 5 shows 5
Toss 6 shows 2
Toss 7 shows 4
Toss 8 shows 2
Toss 9 shows 1
Toss 10 shows 6
```

**Question Simulate the tossing of an unbiased die 100 times and count the numbers of occurrences for each face.**

## Answer

```
1tosses = random.choices(range(1,7),k=100) # generate a list of random integers between 1 and 6, 100 times.
2dict1 = {} # empty dictionary {face: frequency}
3for x in range(1,7):
4 dict1[x] = tosses.count(x)
5 print(f'Face {x} shows {dict1[x]} times')
```

```
Face 1 shows 20 times
Face 2 shows 22 times
Face 3 shows 15 times
Face 4 shows 14 times
Face 5 shows 17 times
Face 6 shows 12 times
```

If you do not wish to store the results in a dictionary but merely want to print out the results, you can use the following shorter code.

```
1tosses = random.choices(range(1,7),k=100)
2for x in range(1,7):
3 print(f'Face {x} shows {tosses.count(x)} times')
```

```
Face 1 shows 20 times
Face 2 shows 22 times
Face 3 shows 15 times
Face 4 shows 14 times
Face 5 shows 17 times
Face 6 shows 12 times
```

### The zip() Function

To loop over two or more sequences (iterables) at the same time, the entries can be paired with the `zip()`

function to create a **list of tuples**.

Example

Using the`zip()`

function to combine 2 iterables into a list of tuples.
```
1names = ["Lily", "Jane", "Tom", "Matt"]
2scores = [62, 86, 75, 91]
3for (name, score) in zip(names, scores):
4 print(f'{name} receives a score of {score}')
```

```
Lily receives a score of 62
Jane receives a score of 86
Tom receives a score of 75
Matt receives a score of 91
```

We can further add the `enumerate()`

function to create an index.

Example

Using both`zip()`

and `enumerate()`

functions in a `for`

loop.
```
1for (i, (name, score)) in enumerate(zip(names, scores)):
2 print(f'{i}: {name} receives a score of {score}')
```

```
0: Lily receives a score of 62
1: Jane receives a score of 86
2: Tom receives a score of 75
3: Matt receives a score of 91
```

`zip()`

can take an arbitrary number of sequences, but the number of elements it produces is determined by the shortest sequence.

Example

`zip()`

function with 3 iterables.
```
1grades = ['C', 'A', 'B'] # assume grade for the last student isn't available.
2for (name, score, grade) in zip(names, scores, grades):
3 print(f'{name} receives a score of {score} and grade {grade}.')
```

```
Lily receives a score of 62 and grade C.
Jane receives a score of 86 and grade A.
Tom receives a score of 75 and grade B.
```