Introduction to Python Programming. Section 9. The ’For’ Loop
9 The ’For’ Loop
9.1 Objectives
This section summarizes various aspects of using the for loop, ranging from trivial to more advanced. The for loop was already mentioned several times in this text, and we will give references to those subsections where relevant. Depending on what you already know, in this section you will either review or learn the following:
- How to parse text strings one character at a time.
- How to split a text string into a list of words.
- How to parse tuples and lists one item at a time.
- More about the built-in function range.
- About using the keyword else with a for loop.
- About list comprehension – a special form of the for loop.
- About iterators, and how to make them.
9.2 Why learn about the for loop?
The for loop is a fundamental construct that all imperative programming languages (Python, C, C++, Fortran, Java, ...) use to iterate over sequences. These sequences can have many different forms:
- Numbers such as 0, 1, 2, ...,
- Text characters in a word.
- Words in a text document.
- Lines, words, or text characters in a text file.
- Entries in a relational database,
- etc.
The for loop is not present in all programming languages though. For example, pure functional programming languages such as Dylan, Erlang, Haskell or Lisp iterate over sequences using recursion instead.
In some imperative languages such as C, C++, Fortran or Java the for loop is sometimes called the counting loop, because its primarily use is to iterate over sequences of numbers.
As usual, Python is very flexible – the for loop can naturally iterate over sequences of numbers, text characters, list / tuple / dictionary items, and over even more general sequences. We will talk about all this in he following subsections.
9.3 Parsing text strings
If you do not know the for loop yet, please read Subsections 4.22 and 4.23 now. In Subsection 4.22 you will see how the keyword for is used to create a for loop, and how the for loop can be used to parse text strings one character at a time. In Subsection 4.23 you will see how the for loop can be used to reverse text strings.
9.4 Splitting a text string into a list of words
In order to parse a text document (text string) one word at a time, it is convenient to split it into a list of individual words first. This was explained in detail in Subsection 4.41. In practise "the devil is in the detail" though, and therefore in Subsection 4.42 we explained in more detail how to take care of punctuation while splitting text strings. Last, lists and list operations were explained in Section 7.
9.5 Parsing lists and tuples (including comprehension)
Lists and tuples can be parsed with the for loop one item at a time analogously to how text strings are parsed one character at a time. This was explained in detail in Subsection 7.10. The for loop works in the same way with tuples – it does not distinguish between lists and tuples at all.
Importantly, list comprehension (which also applies to tuples) is just another form of the for loop. If you do not know how to use list comprehension yet, read Subsections 7.29 – 7.31 now.
9.6 Parsing two lists simultaneously
Sometimes one needs to go over two (or more) lists simultaneously. In this case the standard
approach is to use the built-in function zip which you already know from Subsection 7.28.
As an example, imagine that you have two lists of numbers, and you need to create a list of
their pairs, but only accept pairs where the values are in increasing order. Here is the
code:
9.7 Iterating over sequences of numbers and the built-in function range
The built-in function range was first mentioned in Subsection 6.14. What we have not
shown there though, is that range(N) creates a sequence of integers 0, 1, 2, ..., N-1.
The result is an object of type range,
If needed, it can be cast to a list (or tuple) for display purposes:
A for loop of the form
goes over the corresponding sequence of numbers one number at a time:
1
2
3
4
If needed, one can start the sequence with a nonzero integer:
It is possible to skip over numbers using an optional step argument. For example, the
sequence of all odd numbers between 1 and 11 can be created as follows:
One has to be a bit careful when creating multiples though. Typing range(1, 16, 3)
might appear to be the way to create the sequence of all multiples of 3 between 1 and 15, but
that’s not the case:
A better way to achieve this goal is through list comprehension:
And last, one can create descending sequences using negative step values. This is the
sequence of even numbers 20, 18, ..., 2:
9.8 Parsing dictionaries (including comprehension)
Recall from Subsection 7.40 that a dictionary, in fact, is just a list of tuples of the form (k, v) where k is a key and v the corresponding value. From the same subsection you also know that the list of all keys in a dictionary named D can be extracted via D.keys(), the list of all values via D.values() and the list of all items via D.items(). The latter is a list of two-item tuples.
Hence, parsing a dictionary in fact means to parse a list. One has various options. To go
through the list of all keys, type:
The list of all values can be parsed via:
And to go through the list of all items, type:
or
Importantly, you should make yourself familiar with how comprehension is used for dictionaries. Unless you already did, make sure to read Subsection 7.45 where we showed how to use comprehension to find all keys for a given value, as well as Subsection 7.46 where we explained how to reverse a dictionary using comprehension.
9.9 Nested for loops
Loops can be nested, meaning that the body of a loop can contain another loop (whose body
can contain another loop...). In this case the former is called outer loop and the latter is the
inner loop. With each new nested level the indentation increases. This is shown
in the following example which creates all combinations of given numbers and
letters:
Let’s also show an example of a triple-nested loop which creates all combinations of given
numbers, letters and symbols:
9.10 Terminating loops with the break statement
The break statement can be used to instantly terminate a for or while loop. It is useful in
situations when it no longer makes sense to finish the loop. For example, let’s say that we
have a (very long) list of numbers, and it is our task to find out if all of them are positive. In
this case, the best solution is to parse the list using a for loop, and terminate the loop when a
negative number is found:
If the break statement is used within nested loops, then it only terminates the nearest outer
one. The following example illustrates that. It is analogous to the previous one, but it
analyses a list of lists. You can see that the break statement only terminates the inner loop
because the outer loop goes over the four sub-lists and displays the Boolean value of
allpositive four times:
Importantly, the break statement is just a shortcut. It should be only used to prevent the computer from doing unnecessary operations. Do not get too inventive with this statement. Write your code in such a way that it would work without the break statement too. And finally – make sure to read Subsection 9.12 which is related to the break statement as well.
9.11 Terminating current loop cycle with continue
The continue statement is similar in nature to break, but instead of terminating the loop
completely, it only terminates the current loop cycle, and resumes with the next one. As with
the break statement, keep in mind that continue is a shortcut. Do not force it where
you don’t need it. Unfortunately, you will find many online tutorials which do
exactly the opposite. Such as the following program which leaves out a given letter
from a given text (we are printing in on red background because it’s not a good
code):
The same result can be achieved way more elegantly without the continue statement:
As a matter of fact, every continue statement can be replaced with a condition. So, the main
benefit of using continue is that it "flattens" the code and makes it more readable when
multiple nested conditions are present.
Let’s illustrate this on a primitive translator from British to American English, whose sole
purpose is to leave out the letter ’u’ if it is following ’o’, and when the next character is not ’n’
or ’s’. First, let’s show the version with the continue statement:
n = len(txt)
for i in range(n):
if txt[i] != ’u’:
print(txt[i], end=’’)
continue
if i > 0 and txt[i-1] != ’o’:
print(txt[i], end=’’)
continue
if i < n-1 and txt[i+1] == ’s’:
print(txt[i], end=’’)
continue
if i < n-1 and txt[i+1] == ’n’:
print(txt[i], end=’’)
Below is the version without continue. Notice the complicated structure of the nested
conditions:
For completeness let us add that the continue statement may only occur syntactically nested in a for or while loop, but not nested in a function or class definition or try statement within that loop.
9.12 The for-else statement
In Python, both the for loop and the while loop may contain an optional else branch. The
for-else statement is sort of a mystery, and unfortunately the source of numerous incorrect
explanations in online tutorials. There, one can sometimes read that the else branch is
executed when the for loop receives an empty sequence to parse. Technically, this is
true:
But the else branch is also executed when the sequence is not empty:
So, why would one use the else branch at all?
Well, its purpose is different. As somebody suggested, it should have been named nobreak rather than else, and its purpose would be clear to everybody. Namely, the else branch is executed when the loop has finished regularly (not terminated with the break statement). If the loop is terminated with the break statement, the else branch is skipped. Effectively, this allows us to add code after the for loop which is skipped when the break statement is used. OK, but what is this good for? Let’s show an example.
Imagine that you have a list of values, but some of them can be inadmissible (in our code
below, inadmissible = negative). In particular, if all of them are inadmissible, the program
cannot go on and needs to terminate with a custom error message. The else branch is
perfect for catching this, and throwing that custom error message:
Do not worry about the Exception – for now, it’s there just to throw a custom error message. Exceptions will be discussed in detail in Section 12.
In the "old-fashioned" way, without the else branch, one would have to introduce an
extra Boolean variable named (for example) found to find out whether an admissible value
was found. And, one would have to use an extra condition to inspect the value of
found:
found = False
for n in L:
# If admissible value was found, end the loop:
if n > 0:
found = True
break
if not found:
raise Exception(’Sorry, all values were negative.’)
# Now some code that uses the admissible value:
import numpy as np
result = np.sqrt(n)
print(’The result is’, round(result, 4))
In summary, the use of the else branch has saved us one variable and one condition.
9.13 The for loop behind the scenes – iterables and iterators
At the end of this section we would like to explain the technical details of the for loop, as well as the nature of objects this loop works with. For this, we will need to use some object-oriented terminology, so consider reading Section 14 first.
Let’s begin with a simple for loop:
The text string s is an iterable object or just iterable, meaning that it has a method __iter__. Other examples of iterable objects in Python are lists, tuples, and dictionaries.
Calling s.__iter__() returns an iterator. Equivalently, the iterator can be obtained by calling iter(s) (the built-in function iter calls the __iter__ method of its argument implicitly).
Behind the scenes, the for statement in the above example calls iter(s). For simplicity,
let’s call the returned iterator object iterobj. This object has the method __next__ which
can access the elements in the container s one at a time. When there are no more
elements, __next__ raises a StopIteration exception which tells the for loop to
terminate. The following example shows how the above for loop really works, by
creating an iterator object iterobj from the text string s, and repeating the two
lines
until the StopIteration exception is thrown:
The last line 10 is not executed because the iteration is stopped on line 9. You can also iterate
using the built-in function next which calls __next__ implicitly:
For completeness, let’s perform the above exercise once more for a sample list L = [1, 2,
3]. First, let’s create the corresponding iterator object and use its method __next__ to
iterate through L:
And again, one can use the built-in function next to iterate without revealing the
object-oriented nature of the iteration process:
9.14 Making your own classes iterable
It is easy to add iterator behavior to your classes. Just add an __iter__ method which
returns an object with a __next__ method. If your class already has a __next__ method,
then __iter__ can just return self:
And now let’s use it:
9.15 Generators
Generators are a simple and powerful tool for creating iterators. They are written like regular
functions but use the yield statement whenever they want to return data. Each time the
built-in function next is called on it, the generator resumes where it left off - it remembers all
the data values and which statement was last executed. Anything that can be done
with iterable objects as described in the previous section can also be done with
generators. What makes generators so elegant is that the __iter__ and __next__
methods are created automatically. Let’s show an example to make all this more
clear.
9.16 Functional programming
The more you learn about iterators, the closer you get to functional programming. This is a
fascinating programming style and Python provides modules itertools and
functools to support it. To learn more, we recommend that you start with the Functional
Programming HOWTO in the official Python documentation which can be found
at
Table of Contents
- Preface
- 1. Introduction
- 2. Using Python as a Scientific Calculator
- 3. Drawing, Plotting, and Data Visualization with Matplotlib
- 4. Working with Text Strings
- 5. Variables and Types
- 6. Boolean Values, Functions, Expressions, and Variables
- 7. Lists, Tuples, Dictionaries, and Sets
- 8. Functions
- 9. The ’For’ Loop
- 10. Conditions
- 11. The ’While’ Loop
- 12. Exceptions
- 13. File Operations
- 14. Object-Oriented Programming I – Introduction
- 15. Object-Oriented Programming II – Class Inheritance
- 16. Object-Oriented Programming III – Advanced Aspects
- 17. Recursion
- 18. Decorators
- 19. Selected Advanced Topics