Looping, or iteration, as it's commonly known, sits at the core of computer automation. You write a piece of code and then tell the computer to execute it repeatedly itself, upto a termination point.
Almost all programming languages provide some kind of constructs that enable iteration in the language, out of the box. Python also has a couple of ways to perform iteration — the
for loop, the
while loop, and iterators and generators.
In this unit, we'll explore the first two of these i.e the
for and the
while loops. Iterators and generators are slightly more advanced topics, which we'll cover later in this course.
This chapter is all about the
for loop, how it operates under the hood, how the
range() function works, and much more.
Let's start learning!
The general form of the
for loop is shown below:
for item in iterable: # loop's body
for loop takes a given
iterable, starts at its beginning, assigning its first item to the variable
item. Then it executes the block of code following the loop's header, known as the loop's body.
Here, the variable
item is available for use by the developer, which is, in almost all cases, indeed of use.
Once the body of the loop completes, execution moves back to the header. This time the second item is assigned to
item, and then again the loop's body is executed.
The body completes, execution moves back to the header, the third item of
iterable gets assigned to
item, the body gets executed again, and so on.
All this goes on until
iterable gets exhausted i.e there are no more values to iterate over. Once this happens, the
for loop completes and execution moves to the line following the loop.
Now the only thing unclear at this point may be exactly what is an iterable.
What's an iterable?
To simplify things for now, an iterable is a value that is sequence of items. Following from this definition, a string is an iterable as it's a sequence of characters, a list is an iterable since it's a sequence of elements.
As we shall see in the chapter on iterators.
For this matter,
range() also returns a sequence of numbers, specifically an arithmetic sequence of integers. However, this sequence is not defined all once; rather it's defined lazily.
Anyways, now that we know a couple of sequences, let's apply the
for loop on them.
Remember, that in each iteration of the
for loop, the
item variable is the next item in the given sequence.
We'll start by looping over strings, followed by lists, and finally over the
Recall that strings are a sequence of characters — thereby, iterating over a string using
for would give us each of its characters in each iteration.
An example is shown below.
s = 'Hello' for char in s: print(char)
We iterate over the string
'Hello', and print each of its characters, saved in the variable
Remember that, after the
for keyword, comes the name of the variable to which each item of the given sequence is assigned. This means that it follows the same naming rules as do normal Python variables (well, it is a variable!), and at the same time should be named sensibly.
Here, since each item of the string is a character, we call the variable
char. You could also call it
c as a further abbreviation for 'char'; or
s following from the first character in the word 'string'.
Now whatever happens inside the loop body makes much more sense as we know that
char is a character.
Based on this approach, let's tackle iteration over lists.
Below we iterate over a list of food items, printing each one as we do so.
food_items = ['Pizza', 'Donut', 'Cake'] for food_item in food_items: print(food_item)
As before, note the name
food_item here. Each element of
food_items is a single food item, and likewise it makes sense to call it
Most of the times, this naming convention works i.e the loop variable's name is similar to that of the iterable except for that it's singular whereas the iterable is plural.
A couple more examples are shown below, where this naming convention works well:
for country in countries: pass
for user in users: pass
for task in tasks: pass
Anyways, moving on, similar to lists, it's also possible to iterate over the
tuple data type, as demonstrated below:
nums = (1, 10, 3) for num in nums: print(a)
In many cases, while iterating over a sequence using
for, we need to have the index of the current item.
A simple case is illustrated below:
We have two lists of numbers —
b. Our job is to add consecutive numbers of both these lists and put the sum in a new list
a = [1, 2, 5] b = [10, 11, 30] c =  i = 0 for num in a: c.append(num + b[i]) i += 1
The code above, surely works, however it doesn't look much representable. One list's items are being retrieved in a variable, without using an index, whereas the other one's items are being retrieved using an index in bracket notation.
Not only this, but it also adds an unnecessary variable
i into the program.
A much better, and common way, to solve such index-requiring problems is to loop over
range() function returns an iterable sequence, defined by arguments passed to it.
If only one arg is supplied, it's assumed as the
stop argument. The sequence of integers begins at
0 and goes uptil
stop, excluding it.
range(5) denotes the sequence 0, 1, 2, 3, 4;
range(2) denotes the sequence 0, 1; and
range(0) denotes an empty sequence.
If more than one arg is provided, then the first one acts as
start, and the second one as
end, as follows:
range(start, stop[, step])
This time the sequence begins at
start (including it) and goes uptil
end (once again, excluding it).
range(2, 4) denotes the sequence 2, 3;
range(10, 15) denotes the sequence 10, 11, 12, 13, 14.
The third optional argument
step specifies the difference between consecutive numbers of the sequence. The default
range(0, 6, 2) denotes the sequence 0, 2, 4 (remember that
6 is excluded). Similarly,
range(10, 20, 3) denotes the sequence 10, 13, 16, 19.
A negative step can also be given, but in that case
stop must be lesser than
start, otherwise the loop will never end.
range(5, 0, -1) denotes the sequence 5, 4, 3, 2, 1. Once again, notice that the number
stop is not part of the sequence, since it's excluded.
0at the end, call
range(5, -1, -1). This time, the sequence would run from 5 to 0, excluding
Now that we have a bit of experience with
range(), let's use it with the
We'll take the same problem above of adding corresponding numbers in two lists to create a third list. This time, instead of iterating over
a directly, we iterate over
range(len(a)). That is, we iterate from the first index to the last index of
a = [1, 2, 5] b = [10, 11, 30] c =  for i in range(len(a)): c.append(a[i] + b[i])
Now items of both the lists are retrieved using an index in bracket notation. Everything's consistent and classic!
In programming, it's very common to work with nested loops. There are numerous algorithm out there that utilise single-nested loops; sometimes even double-nested loops.
In this section, we shall cover some of them and see exactly how to work with nested loops.
Let's start by creating an extremely basic single-nested loop:
for i in range(3): for j in range(3): print(i, j)
Can you guess the output made by this code?
What's apparent immediately is that the main
for loop runs 3 times. And for each iteration of the main
for loop, the inner
for loop also iterates 3 times. Together this means 9 prints.
In the first iteration
0. So, what gets printed in the first set of output is
0 1, and
Keep in mind, that while the inner loop is being executed,
i remains constant. It's only
j that changes with each iteration of the inner loop.
Once the inner loop exits, the main loop's body completes and therefore it moves to its second iteration.
i changes to
1. Now the next bunch of prints will have
1 1 and
The second iteration of the main loop completes, paving way for the third one.
i changes to
2. Now the next bunch of prints are
2 1 and
With this, the third iteration of the main loop completes, moving execution back to its header. Here, it's judged that the given sequence i.e
range(3), has been exhausted and likewise the loop is exited.
Altogether, we get the following output:
Let's consider a more practical example.
Matrices are used extensively throughout mathematics in a number of places. In Python, we can initialise a matrix using a nested
Suppose we want to initialise a 3 x 3 matrix with all
0's. The following code accomplishes this:
matrix =  for i in range(3): # create a new row matrix.append() # fill the row for j in range(3): matrix[i].append(0)
In each iteration of the main
for loop, we create a new row in
matrix.append(). Once the row is created, we fill it using the inner
for loop — it adds a
0 in this row, in each of its iterations.
In the end,
matrixis as shown below:
While iterating in a
for loop, there come times when we want to exit the loop before it completes, or simply ignore a given iteration moving to the next one.
These functionalities are provided by the
As the name suggests:
breakmakes execution break out of a given loop.
continuemakes execution continue on with the next iteration, skipping the current one.
Below we demonstrate where could
break possibly be used.
Suppose we have a list of numbers, where we have to search for a
0 and print
"Found", as soon as it's found. Accomplishing this using
for would look something as follows:
nums = [10, 5, 30, 0, 8] for num in nums: if num == 0: print('Found') # end the loop break
If we omit the
break keyword here, the search would continue on even after we've found a
0, which is inefficient.
Now let's see how
Suppose we have a list of numbers where we want to increment every number if it's not a
for, this can be done as follows:
nums = [2, 0, 0, 30, 12, 5] for nums in range(len(nums)): if nums[i] == 0: continue nums[i] += 1
See how the code in line 7 following the
if statement is not encapsulated within an
else block — this is because if the
if's condition gets fulfilled, only then would
continue be executed, as a result directly moving execution to the next iteration and ignoring any following code.
else block here isn't invalid — it's just useless!
Note that this code can be — and in fact, should be — written as follows, removing the
continue keyword and instead laying out the conditional in line 4 such that it proceeds only if
nums[i] is not
nums = [2, 0, 0, 30, 12, 5] for nums in range(len(nums)): if nums[i] != 0: nums[i] += 1
However, for the sake of an example, it's a good way to understand
Moving on, whenever using the
continue keyword, make sure you don't put it in a place where it might prevent other code from running.
For instance, in the code below we want to display a message
'Zero encountered' if
nums[i] is equal to
0, however, we fail to do so:
nums = [2, 0, 0, 30, 12, 5] for nums in range(len(nums)): if nums[i] == 0: continue print('Zero encountered') nums[i] += 1
This is because the
print() statement comes after the
continue keyword. As we've stated before, the moment the interpreter comes across
continue in a loop, it ignores everything that follows and moves to the next iteration.
The correct way is to call the
print() statement before
nums = [2, 0, 0, 30, 12, 5] for nums in range(len(nums)): if nums[i] == 0: print('Zero encountered') continue nums[i] += 1
return keyword inside
As we saw back in the Python Functions chapter, a
return statement inside a function resolves a call to that function with a given value. Not only this, but it also takes execution out of the function.
If a loop comes within a function and
return comes within the loop, then the moment the
return keyword is encountered, execution jumps out of the loop, out of the function.
return is similar to
break in that both take execution out of a loop. The
return keyword additionally resolves the function's call with a given value.
return within loops inside functions is a common practice. Below shown is a very basic example.
def search(val, l): for item in l: if item == val: return True return False
We create a function that searches for a value within a given list and returns
True if it exists, otherwise
False. Both the list and the value to search for are provided as arguments to the function.
Since we need to conduct a search within the list, we need to go over each of its items. That is, we need a loop in the function.
Inside the loop, we check if the current item of the list is equal to
val. If it is, it simply means that the list contains the value. Consequently, we return
There is no point of going beyond once we've found a match, likewise we use
However, if the list doesn't contain the value, the
if statement never executes and likewise when the list gets exhausted by the loop, the statement
return False is executed.
Below, we use this function to search for given values in given lists:
search(10, [0, 2, 3])
search(10, [10, 0, 2])
search('London', ['Paris', 'London', 'Berlin'])
Whenever laying out such loops, make sure that the way you've laid out your conditionals makes sense!