for
loops
for
loops
We’ve seen that while
loops are useful when we know we wish to perform a calculation or task, but we don’t know in advance how many iterations we may need. Thus, while
loops provide a condition, and we loop until that condition (whatever it may be) no longer holds true.
Python has another type of loop which is useful when:
- we know exactly how many iterations we require, or
- we have some sequence (for example, list, tuple, or string) and we wish to perform calculations, tasks, or operations with respect to the elements of the sequence (or some subset thereof).
This new kind of loop is the for
loop. for
loops are so named because they iterate for each element in some iterable. Python for
loops iterate over some iterable. Always.1
What’s an iterable? Something we can iterate over, of course! And what might that be? The sequence types we’ve seen so far (list, tuple, string) are sequences, and these are iterable. We can also produce other iterable objects (which we shall see soon).
Here’s an example. We can iterate over a list, [1, 2, 3]
, by taking the elements, one at a time, in the order they appear in sequence.
>>> numbers = [1, 2, 3]
>>> for n in numbers:
... print(n)
...
1
2
3
See? In our for
loop, Python iterated over the elements (a.k.a. “members”) of the list provided. It started with 1, then 2, then 3. At that point the list was exhausted, so the loop terminated.
If it helps, you can read for n in numbers:
as “for each number, n, in the iterable called ‘numbers’.”
This works for tuples as well.
>>> letters = ('a', 'b', 'c')
>>> for letter in letters:
... print(letter)
...
a
b
c
Notice the syntax: for <some variable> in <some iterable>:
. As we iterate over some iterable, we get each member of the iterable in turn, one at a time. Accordingly, we need to assign these members (one at a time) to some variable.
In the first example, above the variable has the identifier n
.
>>> numbers = [1, 2, 3]
>>> for n in numbers:
... print(n)
...
As we iterate over numbers
(a list), we get one element from the list at a time (in the order they appear in the list). So at the first iteration, n
is assigned the value 1
. At the second iteration, n
is assigned the value 2
. At the third iteration, n
is assigned the value 3
. After the third iteration, there are no more elements left in the sequence and the loop terminates.
Thus, the syntax of a for
loop requires us to give a variable name for the variable which will hold the individual elements of the sequence. For example, we cannot do this:
>>> for [1, 2, 3]:
... print("Hello!")
If we were to try this, we’d get a SyntaxError
. The syntax that must be used is:
for <some variable> in <some iterable>:
# body of the loop, indented
where <some variable>
is replaced with a valid variable name, and <some iterable>
is the name of some iterable, be it a list, tuple, string, or other iterable.
Iterating over a range of numbers
Sometimes we want to iterate over a range of numbers or we wish to iterate some fixed number of times, and Python provides us with a means to do this: the range
type. This is a new type that we’ve not seen before. range
objects are iterable, and we can use them in for
loops.
We can create a new range
object using Python’s built-in function range()
. This function, also called the range
constructor, is used to create range
objects representing arithmetic sequences.2
Before we create a loop using a range
object, let’s experiment a little. The simplest syntax for creating a range
object is to pass a positive integer as an argument to the range
constructor. What we get back is a range
object, which is like a list of numbers. If we provide a positive integer, n
, as a single argument, we get a range
object with n
elements.
>>> r = range(4)
Now we have a range
object, named r
. Let’s get nosy.
>>> len(r)
4
OK. So r
has 4 elements. That checks out.
>>> r[0]
0
>>> r[1]
1
>>> r[2]
2
>>> r[3]
3
>>> r[4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: range object index out of range
We see that the values held by this range
object, are 0, 1, 2, and 3, in that order.
Now let’s use a range
object in a for
loop. Here’s the simplest possible example:
>>> for n in range(4):
... print(n)
What do you think this will print?
- The numbers 1 through 4?
- The numbers 0 through 4? (since Python is zero-indexed)
- The numbers 0 through 3? (since Python slices go up to, but do not include, the stop index)
Here’s the answer:
>>> for n in range(4):
... print(n)
...
0
1
2
3
Zero through three. range(n)
with a single integer argument will generate an arithmetic sequence from 0 up to, but not including, the value of the argument.
Notice, though, that if we use range(n)
our loop will execute n
times.
What if we wanted to iterate integers in the interval [5, 10]? How would we do that?
>>> for n in range(5, 11):
... print(n)
...
5
6
7
8
9
10
The syntax here, when we use two arguments, is range(<start>, <stop>)
, where <start>
and <stop>
are integers or variables with integer values. The range
will include integers starting at the start value up to but not including the stop value.
What if, for some reason, we wanted only even or odd values? Or what if we wanted to count by threes, or fives, or tens? Can we use a different step size or stride? Yes, of course. These are all valid arithmetic sequences. Let’s count by threes.
>>> for n in range(3, 19, 3):
... print(n)
...
3
6
9
12
15
18
This three argument syntax is range(<start>, <stop>, <stride>)
. The last argument, called the stride or step size corresponds to the difference between terms in the arithmetic sequence (the default stride is 1).
Can we go backward? Yup. We just use a negative stride, and adjust the start and stop values accordingly.
>>> for n in range(18, 2, -3):
... print(n)
...
18
15
12
9
6
3
This yields a range
which goes from 18, down to but not including 2, counting backward by threes.
So you see, range()
is pretty flexible.
What if I just want to do something many times and I don’t care about the members in the sequence?
No big deal. While we do require a variable to hold each member of the sequence or other iterable we’re iterating over, we aren’t required to use it in the body of the loop. There is a convention, not required by the language, but commonly used, to use an underscore as the name for a variable that we aren’t going to use or don’t really care about.
>>> for _ in range(5):
... print("I don't like Brussles sprouts!")
...
I don't like Brussles sprouts!
I don't like Brussles sprouts!
I don't like Brussles sprouts!
I don't like Brussles sprouts!
I don't like Brussles sprouts!
(Now you know how I feel about Brussels sprouts.)
Comprehension check
What is the evaluation of
sum(range(5))
?What is the evaluation of
max(range(10))
?What is the evaluation of
len(range(0, 10, 2))
Copyright © 2023–2025 Clayton Cafiero
No generative AI was used in producing this material. This was written the old-fashioned way.
Footnotes
for
loops in Python work rather differently than they do in many other languages. Some languages use counters, and thusfor
loops are count-controlled. For example, in Java we might writefor (int i = 0; i < 10; ++i) { // do something }
In this case, there’s a counter,
i
, which is updated at each iteration of the loop. Here we update by incrementingi
using++i
(which in Java incrementsi
). The loop runs so long as the control conditioni < 10
is true. On the last iteration, withi
equal to nine,i
is incremented to ten, then the condition no longer holds, and the loop exits. This is not howfor
loops work in Python! Pythonfor
loops always iterate over an iterable.↩︎An arithmetic sequence, is a sequence of numbers such that the difference between any number in the sequence and its predecessor is constant. 1, 2, 3, 4, is an arithmetic sequence because the difference between each of the terms is 1. Similarly, 2, 4, 6, 8, is an arithmetic sequence because the difference between each term is 2. Python range objects are restricted to arithmetic sequences of integers.↩︎