6: Iteration#
What are loops and why should we care about them?#
Loops are a fundamental building block of computational solutions to problems. They, like conditionals, are an example of control flow: structures in our program that control what happens when. Recall that control flow allows you to control when/whether (conditional) and how many times (loops) you do things
Loops are especially useful because it’s hard to build programs without a concise way to instruct the computer to do repeated actions.
Here are some simple examples. Try to think of how you might solve these without loops!
Put 6 cups of flour into a box
Stir occasionally until the sauce starts to reduce
With loops these get a LOT easier to specify, and become more robust and reusable too.
Example (not real!) program: Put 6 cups of flour into a box
1def scoop_into_box():
2 print("Scoop into box")
3
4# scoop into the box 6 times
5for i in range(6):
6 scoop_into_box()
Scoop into box
Scoop into box
Scoop into box
Scoop into box
Scoop into box
Scoop into box
Example (not real!) program: Stir occasionally until the sauce starts to reduce (i.e., is thick)
1# stir the sauce until it is thick
2while check_sauce() != "thick":
3 stir()
Loops also enable many useful algorithms/patterns that go nicely with lists. You’ll be practicing and applying them in PCEs and Projects this module!
For example:
Searching through a list
Filtering a list of items
Counting occurrences in some collection
Continuing with our running example for this module, here are loops in the context of a program:
1# key variables:
2# the input LIST of strings
3inputs = [
4 "hello sarah@umd.edu",
5 "from: joelchan@umd.edu",
6 "some other text that doesn't have an email"
7]
8# a LIST to store the email addresses
9emails = []
10
11# LOOP over every text input
12for text_input in inputs:
13
14 # extract an email address
15 # split the text into subsets
16 chunks = text_input.split()
17
18 # LOOP over the list of chunks to check each one
19 for chunk in chunks:
20 # check if it has @ and .
21 if "@" in chunk and "." in chunk:
22 # put the chunk in the email list
23 emails.append(chunk)
24# give the email address back to the user
25print(emails)
['sarah@umd.edu', 'joelchan@umd.edu']
Definite loops (for loops)#
Quite often we have a list of items of the lines in a file - effectively a finite set of things. We can write a loop to do some operation once for each of the items in a set using the Python for construct.
These loops are called “definite loops” because they execute an exact number of times. We say that “definite loops iterate through the members of a set”
Use definite/for when you know in advance how many times you want to do something.
This is the use case in our running example.
Other examples:
Do an action N times
Take M steps
Do something for every item in a finite list
Anatomy of a definite (for) loop in Python#
Let’s take a closer look.
The iteration variable “iterates” through the sequence (ordered set)
The block (body) of code is executed once for each value in the sequence
The iteration variable moves through all of the values in the sequence
1nums = [5, 4, 3, 2, 1]
2# here, n is the iteration variable
3
4n = nums[0]
5n = nums[1]
6n = nums[2]
7
8for n in nums:
9 print("taking something from the list")
10 print(n) # block/body
taking something from the list
5
taking something from the list
4
taking something from the list
3
taking something from the list
2
taking something from the list
1
1nums = [5, 4, 3, 2, 1]
2# here, i is the iteration variable
3for i in nums:
4 print(i)
5 new_num = i*20
6 print(new_num) # block/body
5
100
4
80
3
60
2
40
1
20
The iteration variable is a variable: this means you can name it whatever you like, subject to the basic syntax rules and of course our heuristic to name things to make the logic of the program legible.
What is the iteration variable here?
1nums = [5, 4, 3, 2, 1]
2for a_number in nums:
3 new_num = a_number*20
4 print(new_num) # block/body
100
80
60
40
20
What is the iteration variable here?
1nums = [5, 4, 3, 2, 1]
2for num in nums:
3 if num % 2 == 0: # check if even
4 print(num) # block/body
4
2
What is the iteration variable here?
1for name in ["john", "terrell", "qian", "malala"]:
2 print(name)
john
terrell
qian
malala
1# the range function produces an iterable sequence of numbers
2# that start at the optional first argument, and stop before
3# the required second argument
4# https://www.w3schools.com/python/ref_func_range.asp
5list(range(0,5))
[0, 1, 2, 3, 4]
1# use this if you want to specify doing something N times
2# e.g., here, take a step 5 times
3for i in range(7):
4 print("I has the value", i)
5 print("Taking a step")
I has the value 0
Taking a step
I has the value 1
Taking a step
I has the value 2
Taking a step
I has the value 3
Taking a step
I has the value 4
Taking a step
I has the value 5
Taking a step
I has the value 6
Taking a step
1# use this if you want to specify doing something N times
2# e.g., here, take a step 5 times
3for i in [0,1,2,3,4]:
4 print("I has the value", i)
5 print("Taking a step")
I has the value 0
Taking a step
I has the value 1
Taking a step
I has the value 2
Taking a step
I has the value 3
Taking a step
I has the value 4
Taking a step
1# scoop 6 cups
2steps = 6
3for step in range(steps):
4 print("scooping cup number", step+1)
scooping cup number 1
scooping cup number 2
scooping cup number 3
scooping cup number 4
scooping cup number 5
scooping cup number 6
PRACTICE: how would you write a loop to print “hello” 3 times?
1# practice: your code here
PRACTICE: print out each name in this list
1names = ["Joel", "John", "Jane", "Jamie", "John", "Michael", "Sarah", "Joseph", "Chris", "Ray"]
2
3# your loop here
PRACTICE: print out each donation in this list
1donations = [
2 0.00, 10.00, 25.00, 50.00, 75.00, 100.00, 250.99, 500.00, 1000.00, 2500.00,
3 5000.00, 7500.50, 10000.00, 0.00, 12500.75, 15000.00, 20000.99, 25000.00, 30000.00,
4 40000.00, 50000.00, 243.29, 0.00
5]
6
7
8# your loop here
To get a feeling for what is going on, try copy-pasting one of these programs into python tutor and inspect it!
Practice: Code Tracing with Definite Loops#
For each problem below, predict what the code will print before running the cell.
Trace 1#
1for i in range(4):
2 print(i)
What does this print?
A)
1 2 3 4B)
0 1 2 3C)
0 1 2 3 4D)
1 2 3
Answer:
B) 0 1 2 3 (each on its own line)
range(4) produces 0, 1, 2, 3 — it starts at 0 and stops before 4. If you picked A, remember that range starts at 0 by default. If you picked C, remember the upper bound is exclusive.
Trace 2#
1count = 0
2for item in ["a", "b", "c", "d", "e"]:
3 count = count + 1
4print(count)
What does this print?
A)
4B)
5C)
eD)
0
Answer:
B) 5
The loop runs once per item in the list (5 items), and each time it increments count by 1. Note that print(count) is outside the loop (not indented under for), so it only prints the final value. If you picked A, you may be thinking range stops before the end — but this loop iterates over a list directly, once per item.
Trace 3#
1total = 0
2for num in [10, 20, 30]:
3 total = total + num
4 print(total)
What does this print?
A)
60B)
10 20 30C)
10 30 60D)
0 10 30
Answer:
C) 10, 30, 60 (each on its own line)
total starts at 0. After the first iteration: 0 + 10 = 10. After the second: 10 + 20 = 30. After the third: 30 + 30 = 60. Because print(total) is inside the loop, it prints the running total each time. If you picked A, that’s what you’d get if the print were outside the loop.
Trace 4#
1for i in range(3):
2 print("hello")
3print("done")
What does this print?
A)
helloonce, thendoneB)
hellothree times, thendoneC)
hellothree times (nodone)D)
hellofour times, thendone
Answer:
B) hello three times, then done
range(3) gives 0, 1, 2 — three values, so the loop body runs 3 times. print("done") is outside the loop (not indented), so it runs once after the loop finishes. If you picked C, look at the indentation — print("done") is at the same level as for, not inside the body.
Trace 5#
1result = []
2for n in [3, 1, 4]:
3 result.append(n * 2)
4print(result)
What does this print?
A)
[3, 1, 4]B)
[6, 2, 8]C)
[6]D)
[8]
Answer:
B) [6, 2, 8]
Each iteration multiplies the current item by 2 and appends it to result. So: 3*2=6, 1*2=2, 4*2=8. If you picked C or D, remember the loop runs for every item in the list, not just the first or last.
Trace 6#
1for i in range(1, 6):
2 print(i)
What does this print?
A)
0 1 2 3 4 5B)
1 2 3 4 5C)
1 2 3 4 5 6D)
0 1 2 3 4
Answer:
B) 1 2 3 4 5 (each on its own line)
range(1, 6) starts at 1 and stops before 6. If you picked C, remember the upper bound is always exclusive. If you picked A, note that the first argument changes the start from the default of 0.
Trace 7#
1x = 10
2for i in range(3):
3 x = x - i
4print(x)
What does this print?
A)
10B)
7C)
4D)
1
Answer:
B) 7
Trace it step by step: i=0: x = 10 - 0 = 10. i=1: x = 10 - 1 = 9. i=2: x = 9 - 2 = 7. The print is outside the loop, so only the final value 7 is printed. The tricky part is that i changes each iteration (0, 1, 2), and x carries forward.
Trace 8#
1words = ["hi", "there", "world"]
2for word in words:
3 if len(word) > 3:
4 print(word)
What does this print?
A)
hi there worldB)
there worldC)
thereD) Nothing (no output)
Answer:
B) there and world (each on its own line)
The loop checks each word’s length. "hi" has length 2 (not > 3, skipped). "there" has length 5 (printed). "world" has length 5 (printed). If you picked C, you may have thought > means “strictly greater” and "world" is exactly 5 — but 5 > 3 is True!
Trace 9#
1nums = [5, 10, 15, 20]
2for i in range(len(nums)):
3 print(i, nums[i])
What does this print?
A)
5 10 15 20B)
0 5,1 10,2 15,3 20C)
1 5,2 10,3 15,4 20D)
0 10,1 15,2 20
Answer:
B) 0 5, 1 10, 2 15, 3 20 (each pair on its own line)
range(len(nums)) is range(4), which gives 0, 1, 2, 3. Each iteration, i is the index and nums[i] is the value at that index. This is the index-based iteration pattern — useful when you need both the position and the value.
Trace 10#
1result = ""
2for letter in ["P", "y", "t", "h", "o", "n"]:
3 result = result + letter
4print(result)
5print(len(result))
What does this print?
A)
P y t h o nand6B)
Pythonand6C)
nand1D)
Pythonand11
Answer:
B) Python and 6
String concatenation (+) joins without spaces. Each iteration adds one letter to result: "" → "P" → "Py" → "Pyt" → … → "Python". The final string has 6 characters. This is the accumulator pattern — building up a result across iterations, just like accumulating a sum, but with strings instead of numbers.
Common design patterns with definite loops#
Counting#
A common situation: you have a list of stuff, and you want to count how many times a certain kind of thing shows up in that list.
If you want to count occurrences based on a simple exact match, you can use the .count() list method.
1names = ["Joel", "John", "Jane", "Jamie", "John", "Michael", "Sarah", "Joseph", "Chris", "Ray"]
2# count how many times "John" is in here
3names.count("John")
2
But often you want to count based on something more complex than an exact match. For example, let’s say I want to count the number of “high performers” in a list of scores (where high performing means score of 95 or greater).
Iteration is a really helpful way to do this.
Here’s an example program for counting how many scores are above a user-defined threshold.
1#
2scores = [65, 78, 23, 97, 100, 25, 95] # input list
3
4threshold = 67 # score of C
5
6n_highperformers = 0 # define the count variable, initialize to 0
7
8# go through each item
9for score in scores:
10 # check if meets my criteria for being counted
11 if score >= threshold:
12 # if so, increase count
13 n_highperformers += 1
14print(n_highperformers)
4
The generic pattern (or algorithm) is something like this:
# initialize count variable
# for every item in list
# check if meets my criteria for being counted
# if so, increase count
PRACTICE: count how many “small dollar donors” (less than 100) or “big dollar donors” (1000 or up) are in this list of donations. Be careful! 0 is not a small dollar donation!
1donations = [
2 0.00, 10.00, 25.00, 50.00, 75.00, 100.00, 250.99, 500.00, 1000.00, 2500.00,
3 5000.00, 7500.50, 10000.00, 0.00, 12500.75, 15000.00, 20000.99, 25000.00, 30000.00,
4 40000.00, 50000.00, 243.29, 0.00
5]
PRACTICE: count how many names are short (e.g., 4 characters or less)? Or long (e.g., more than 5 characters).
1names = ["Joel", "John", "Jane", "Jamie", "Johnny", "Michaela", "Sarah", "Joseph", "Chris", "Ray"]
2
3# your code here
PRACTICE: check how many times we have a “banned” name. hint: how do we check if an item is in a list?
1names = ["Joel", "John", "Jane", "Jamie", "Johnny", "Michaela", "Sarah", "Joseph", "Chris", "Ray"]
2
3banned = ["Joel", "Chris"]
4
5# your code here
Filtering#
A closely related situation is where we want to not only count, but also “grab” or filter the things that meet our criteria.
We’d want to create a new list, and make sure we have a bit of code that adds to that new list based on the criteria we have.
Example: grab all scores that cross our threshold.
1scores = [65, 82, 23, 97, 100, 95] # input list to be filtered
2
3threshold = 93 # the criterion
4
5# initialize empty list to hold filtered items
6to_grab = []
7
8# go through each item
9for score in scores:
10 # check if item meets criteria for being filtered
11 if score >= threshold:
12 # if so, add the item to the output list
13 to_grab.append(score)
14
15print(to_grab)
[97, 100, 95]
The generic pattern is something like this:
# initialize empty list to hold filtered items
# go through each item
# check if item meets criteria for being filtered
# if so, add the item to the output list
PRACTICE: Let’s modify our program above to grab the small dollar donations and put them in a new list so we can count how many we have and what the total and average small dollar donation is.
1donations = [
2 0.00, 10.00, 25.00, 50.00, 75.00, 100.00, 250.99, 500.00, 1000.00, 2500.00,
3 5000.00, 7500.50, 10000.00, 0.00, 12500.75, 15000.00, 20000.99, 25000.00, 30000.00,
4 40000.00, 50000.00, 243.29, 0.00
5]
PRACTICE: Let’s modify our program above to grab only the names that aren’t in our banned list.
1names = ["Joel", "John", "Jane", "Jamie", "Johnny", "Michaela", "Sarah", "Joseph", "Chris", "Ray"]
2
3banned = ["Joel", "Chris"]
4
5# your code here
Just a reminder that you can use the filter() function if you’re curious, BUT YOU DO NOT HAVE TO. This is just an extra thing if you’re curious
1scores = [65, 82, 23, 97, 100, 95] # input list to be filtered
2threshold = 80
3
4def meets_critera(x):
5 return x >= threshold
6
7to_grab = list(filter(meets_critera, scores))
8print(to_grab)
[82, 97, 100, 95]
Mapping / transforming#
Finally, sometimes you want to modify some/all elements in a list into a new list. An example might be data cleaning, or data transformation.
EXAMPLE: Convert a list of scores (on scale of 0 to 100) to proportions.
1# input list
2scores = [65, 82, 23, 97, 100, 95]
3
4# output list
5proportions = []
6
7# go through every item
8for score in scores:
9 # apply the transformation
10 proportion = score/100
11 # add the transformed value to the output list
12 proportions.append(proportion)
13proportions
[0.65, 0.82, 0.23, 0.97, 1.0, 0.95]
A variant of the program that’s a bit more concise (does the same thing):
1# input list
2scores = [65, 82, 23, 97, 100, 95]
3
4# output list
5proportions = []
6
7# go through every item
8for score in scores:
9 # apply the transformation
10 # add the transformed value to the output list
11 proportions.append(score/100)
12proportions
[0.65, 0.82, 0.23, 0.97, 1.0, 0.95]
The generic pattern is something like this
# initialize empty list to hold transformed items
# go through each item in the input list
# apply transformation to item
# add transformed item to transformed items list
PRACTICE: Change outliers (those above 1000) to missing (“NA”)
1scores = [65, 82, 2323, 97, 100, 95000]
2
3# your code here
PRACTICE: Change the list from scores to letter grades, using the score to letter grade mappings from our syllabus (e.g., 93 and above is A).
1scores = [65, 82, 23, 97, 100, 95] # input list to be filtered
2
3# your code here
As an extra thing to try, you can use the map() built-in function to do this too!
1scores = [65, 82, 23, 97, 100, 95]
2# convert to proportions
3proportions = list(map(lambda x: x/100, scores))
4proportions
[0.65, 0.82, 0.23, 0.97, 1.0, 0.95]
1def score_to_grade(a_score):
2 # apply transformation to item
3 if a_score >= 93:
4 grade = "A"
5 elif a_score >= 83:
6 grade = "B"
7 elif a_score >= 73:
8 grade = "C"
9 elif a_score >= 63:
10 grade = "D"
11 else:
12 grade = "F"
13 return grade
14
15scores = [65, 82, 23, 97, 100, 95]
16# convert to letter grades
17grades = list(map(score_to_grade, scores))
18grades
['D', 'C', 'F', 'A', 'A', 'A']
Coordinated iteration across one or more sequences#
How do you go through the elements of a list, index by index?
In our for loops above that iterated through items in a list, we typically had an iteration variable that directly stored an item from the list at each step.
But we can also define an iteration variable that iterates through a list of indices (remember what indices are in a list? they’re positions in the list!). This will allow us to then use Indexing to grab an item from that index position from our target list.
Here’s an example:
1scores = [65, 82, 23, 97, 100, 95]
2
3# iterate through a list of indices that is of the same length as the `scores` list
4# by convention, people usually name the iteration variable `i`
5for i in range(len(scores)):
6 # and print the score at that index
7 print(scores[i])
65
82
23
97
100
95
And another one:
1# basic iteration through a list using indices
2names = ["Joel", "John", "Lane", "Jamie", "Freddy"]
3
4# make a list of numbers that start at 0, and stop before
5# the length of the names list
6# and go through every number in that list
7for index in range(len(names)):
8 # use the number as an index for the names list
9 name = names[index]
10 # do something with the item at that index
11 print(name)
Joel
John
Lane
Jamie
Freddy
We can extend this pattern to iterate through multiple lists at the same time in a coordinated way. This works as long as the lists are all of the same length: we’ll only need to define a list of indices that are the same length as one of the lists and avoid running into an IndexError (e.g., trying to grab an item from an index position that doesn’t exist from one of the lists that is shorter than the others!)
Here’s an example:
1# basic iteration through a list using indices
2names = ["Joel", "John", "Lane", "Jamie", "Freddy"]
3eligibilities = [True, False, True, True, False]
4
5# make a list of numbers that start at 0, and stop before
6# the length of the names list
7# and go through every number in that list
8for index in range(len(names)):
9 # use the number as an index for the first list
10 name = names[index]
11 # use the same number as an index for the second list
12 eligible = eligibilities[index]
13 # do something with the items at the same index position for both lists
14 print(name, eligible)
Joel True
John False
Lane True
Jamie True
Freddy False
The generic pattern is something like this:
# make a list of numbers that start at 0, and stop before
# the length of one of the lists (assuming they are the same length!)
# and go through every number in that list
# use the number as an index for the first list
# use the same number as an index for the second list
# do something with the items at the same index position for both lists
One of problems for Project 2 (the rock paper scissors problem) relies on precisely this design pattern, since you need to go through two lists of moves in lockstep (first move from both players, then second move from both players, and so on).
Indefinite loops (while loops)#
Sometimes you want to repeat actions, but you don’t know in advance how many times you want to repeat. But you do have a clear idea of when to stop (or equivalently, when to keep going). In this situation, you can use indefinite loops.
Examples:
Keep going until I tell you to stop
Keep stirring until the sauce thickens
Keep taking candy from the box until your bucket is full or the box is empty
Use indefinite/while when you don’t know in advance how many times you want to do something, but can clearly express when to stop (or keep going).
Anatomy of an indefinite (while) loop in Python#
The while loop checks a condition before each iteration. The loop keeps running as long as the condition is True, and stops as soon as it becomes False.
The condition is a Boolean expression that controls whether the loop continues
while steps < limit:→ keep going as long as steps is under the limitwhile not found:→ keep going as long as we haven’t found it yetIn both cases, the loop stops when the expression evaluates to
False
The block (body) of code is executed once for each iteration in the loop
Condition update: It is essential that the body of the loop does something that can eventually make the condition
False— otherwise the loop runs forever!
1# keep taking steps until you hit a limit
2steps = 0
3limit = 10
4# condition: keep going as long as steps is under the limit
5while steps < limit:
6 # body of the loop (aka do some stuff)
7 print("Taking a step", steps)
8 # update: bring steps closer to limit so the condition eventually becomes False
9 steps += 1
10print("Done!")
Taking a step 0
Taking a step 1
Taking a step 2
Taking a step 3
Taking a step 4
Taking a step 5
Taking a step 6
Taking a step 7
Taking a step 8
Taking a step 9
Done!
Notice how this is similar to a for loop — the same stepping logic can be written either way:
1# the same thing with a for loop
2for i in range(10):
3 print("Taking a step", i)
4print("Done!")
Taking a step 0
Taking a step 1
Taking a step 2
Taking a step 3
Taking a step 4
Taking a step 5
Taking a step 6
Taking a step 7
Taking a step 8
Taking a step 9
Done!
The difference is that the for loop handles the counter (i) automatically, while the while loop requires you to initialize and update steps yourself. Use while when you don’t know in advance how many iterations you need.
Generic pattern:
# initialize variables
while condition:
# body of the loop (aka do some stuff)
# update something so the condition eventually becomes False
Here’s a guessing game example. This code uses input(), so it needs to be run interactively (e.g., copy-paste into a Python terminal):
guess = input("Try to guess the number between 1 and 10, or say `exit` to quit")
number = 5
found = False
while guess != "exit" and not found:
if int(guess) == number:
print("You got it!")
found = True
else:
guess = input("Try to guess the number between 1 and 10, or say `exit` to quit")
Again, it’s helpful to copy-paste one of these programs into python tutor to get an intuition for what is going on.
Some applications of indefinite loops#
Generically: keep doing something until…#
Keep adding characters to a string until it is a defined length, e.g., 10
1input_string = "abc"
2
3# while condition: keep going as long as...
4while len(input_string) < 10:
5 # body of the loop (aka do some stuff)
6 input_string = input_string + "a"
7 print(input_string)
8 # the condition update is built in: we're modifying input_string, which is what the while checks
9
10print(input_string)
11print(len(input_string))
abca
abcaa
abcaaa
abcaaaa
abcaaaaa
abcaaaaaa
abcaaaaaaa
abcaaaaaaa
10
Keep adding “.” to the string until it is 13 characters long.
1input_string = "abc"
2
3# while condition: keep going as long as...
4while len(input_string) < 13:
5 # body of the loop (aka do some stuff)
6 input_string = input_string + "."
7 # the condition update is built in: we're modifying input_string, which is what the while checks
8
9print(input_string)
abc..........
PRACTICE: Keep dividing a number by 2 (using integer division //) until the result is 0.
1num = 12000
2
3# your code here
PRACTICE: Go through a list of people until we have 2 people named “John”.
1names = ["Joel", "John", "Jane", "Jamie", "John", "Michaela", "Sarah", "John", "Chris", "John"]
2
3# your code here
Basic user interfaces: keep running program until user stops us#
Guessing game. This code uses input(), so it needs to be run interactively (e.g., copy-paste into a Python terminal):
guess = input("Try to guess the number between 1 and 10, or say `exit` to quit")
number = 5
found = False
while guess != "exit" and not found:
if int(guess) == number:
print("You got it!")
found = True
else:
guess = input("Try to guess the number between 1 and 10, or say `exit` to quit")
print("Thanks for playing!")
Notice how we use two conditions joined by and: the loop keeps going as long as the user hasn’t typed “exit” AND we haven’t found the number yet. Either condition becoming False will stop the loop.
Later in this chapter we’ll learn about break, which gives us another way to exit a loop early.
All of the definite loops we saw earlier can be implemented with indefinite loops!#
1# input list
2scores = [65, 82, 23, 97, 100, 95]
3
4# output list
5proportions = []
6
7# initialize index variable
8i = 0
9# while condition: keep going as long as...:
10# i is less than the length of the list?
11while i < len(scores):
12 # body of the loop (aka do some stuff)
13 # apply the transformation
14 proportion = scores[i]/100
15 # add the transformed value to the output list
16 proportions.append(proportion)
17 # update: increment i so the condition eventually becomes False
18 i += 1
19
20proportions
[0.65, 0.82, 0.23, 0.97, 1.0, 0.95]
Practice: Code Tracing with Indefinite Loops#
Predict the output of each code snippet before running the cell!
Trace 1#
1x = 10
2while x > 0:
3 print(x)
4 x = x - 3
What is the output?
A)
10741B)
1074C)
741D)
10741-2
Answer:
A) 10 7 4 1
Trace the value of x at the start of each iteration:
x = 10: 10 > 0 isTrue, prints10,xbecomes 7x = 7: 7 > 0 isTrue, prints7,xbecomes 4x = 4: 4 > 0 isTrue, prints4,xbecomes 1x = 1: 1 > 0 isTrue, prints1,xbecomes -2x = -2: -2 > 0 isFalse, loop ends
The print happens before the subtraction, and -2 is never printed because the condition is checked at the top of each iteration.
Trace 2#
1count = 0
2n = 1
3while n < 100:
4 n = n * 2
5 count = count + 1
6print(n, count)
What is the output?
A)
64 6B)
128 7C)
100 7D)
256 8
Answer:
B) 128 7
n doubles each iteration: 1 → 2 → 4 → 8 → 16 → 32 → 64 → 128. When n becomes 128, the condition 128 < 100 is False, so the loop ends. It took 7 doublings, so count is 7. Note that n ends at 128, not 64 — the doubling to 128 happens inside the loop before the condition is checked again.
Trace 3#
1items = [4, 7, 2, 9, 1]
2i = 0
3while i < len(items) and items[i] != 9:
4 i += 1
5print(i)
What is the output?
A)
9B)
4C)
3D)
5
Answer:
C) 3
The loop increments i until it either reaches the end of the list or finds 9:
i = 0:items[0]is 4, not 9, soibecomes 1i = 1:items[1]is 7, not 9, soibecomes 2i = 2:items[2]is 2, not 9, soibecomes 3i = 3:items[3]is 9, soitems[i] != 9isFalse, loop ends
i is 3, which is the index where 9 was found — not the value 9 itself.
Trace 4#
1nums = [3, 5, 2, 8, 6]
2i = 0
3total = 0
4while i < len(nums):
5 if nums[i] % 2 == 0:
6 total += nums[i]
7 i += 1
8print(total)
What is the output?
A)
24B)
16C)
10D)
8
Answer:
B) 16
The loop goes through every item in the list (like a for loop would), but only adds even numbers to total:
3: odd, skip
5: odd, skip
2: even, total = 0 + 2 = 2
8: even, total = 2 + 8 = 10
6: even, total = 10 + 6 = 16
Note that i += 1 is outside the if, so i always increments — otherwise we’d have an infinite loop!
Trace 5#
1words = ["cat", "dog", "bird", "fish"]
2result = []
3i = len(words) - 1
4while i >= 0:
5 result.append(words[i])
6 i -= 1
7print(result)
What is the output?
A)
["cat", "dog", "bird", "fish"]B)
["fish", "bird", "dog", "cat"]C)
["fish"]D)
["bird", "dog", "cat"]
Answer:
B) ["fish", "bird", "dog", "cat"]
This loop walks through the list backwards by starting i at the last index (3) and decrementing:
i = 3: appends"fish",ibecomes 2i = 2: appends"bird",ibecomes 1i = 1: appends"dog",ibecomes 0i = 0: appends"cat",ibecomes -1i = -1: -1 >= 0 isFalse, loop ends
The result is the original list in reverse order.
Trace 6#
1values = [1, 3, 5, 7, 9, 11]
2i = 0
3while i < len(values):
4 i += 2
5print(i, len(values))
What is the output?
A)
4 6B)
6 6C)
5 6D)
10 6
Answer:
B) 6 6
The loop increments i by 2 each time, but doesn’t do anything else with the list:
i = 0: 0 < 6 isTrue,ibecomes 2i = 2: 2 < 6 isTrue,ibecomes 4i = 4: 4 < 6 isTrue,ibecomes 6i = 6: 6 < 6 isFalse, loop ends
i is 6, which equals len(values). This is a common pattern — when a while i < len(...) loop ends normally, i equals the length of the list.
Breaking a loop with the break statement#
The break statement ends the current loop immediately and jumps to the statement right after the loop. It’s like an emergency exit that can happen anywhere in the body of the loop.
Here’s an example: search for “John” in a list, and stop as soon as we find him (no need to keep checking the rest).
1found = False # default is we haven't found it
2names = ["Joel", "John", "Jane", "Jamie", "Lisa", "Anna", "Fred"]
3for name in names:
4 print(name)
5 if name == "John":
6 found = True
7 print("Found!")
8 break
9print("We're done with the loop")
10if found:
11 print("Found john!")
12else:
13 print("Didn't find john")
Joel
John
Found!
We're done with the loop
Found john!
Notice that the loop only printed “Joel” and “John” — it didn’t continue through the rest of the list because break exited the loop early.
A common pattern with break is while True: a loop that runs forever unless a break is reached. This is handy for user interfaces where you don’t know when the user will want to stop. This code uses input(), so it needs to be run interactively (e.g., copy-paste into a Python terminal):
while True:
line = input('> ')
if line == 'done':
break
print(line)
print('Done!')
Common errors#
Indentation is key!#
The way that Python knows what counts as the body of code for a loop (whether definite or indefinite) is through indentation.
You must indent all code that goes in the body underneath the for/while statement (after the colon).
If you fail to indent the first line of code in the body, you will get an IndentationError.
If you fail to indent anything after the first line of code in the body, you will be committing a semantic error: Python will not alert you because it is legal code. But your program will not do what you intend it to do.
1for i in range(5):
2print(i)
Cell In[57], line 2
print(i)
^
IndentationError: expected an indented block after 'for' statement on line 1
1# i want to step through a list of numbers, multiply each of them by 5 and print them out
2nums = [1,2,3,4,5]
3for num in nums:
4 new_num = num*5
5print(new_num)
25
Notice how this only prints 25 (the last value), not all five values! That’s because print(new_num) is not indented under the for, so Python treats it as code that runs once after the loop finishes, not during each iteration. To fix this, indent print(new_num) to match new_num = num*5.
IndexError when looping through a list#
This comes up mostly with while loops. So, while it’s possible to do any for loop with a while loop, you want to be careful with it.
1names = ["Joel", "John", "Jane", "Jamie", "John"]
2to_grab = [] # output list, initialize to empty list
3
4index = 0 # set initial index to zero
5while index < 10: # BUG: 10 is larger than the list! should be len(names)
6 print(index)
7 name = names[index] # get the name at this index
8 if name == "John": # check if is john / meets my criteria for being filtered
9 to_grab.append(name) # add the item to the output list
10 index += 1 # increment the index
11
12# print out the result
13print(to_grab)
0
1
2
3
4
5
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[59], line 7
5 while index < 10: # BUG: 10 is larger than the list! should be len(names)
6 print(index)
----> 7 name = names[index] # get the name at this index
8 if name == "John": # check if is john / meets my criteria for being filtered
9 to_grab.append(name) # add the item to the output list
IndexError: list index out of range
1# basic iteration through a list using indices
2names = ["Joel", "John", "Lane", "Jamie", "Freddy"]
3
4for index in range(6):
5 name = names[index]
6 print(index, name)
0 Joel
1 John
2 Lane
3 Jamie
4 Freddy
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[60], line 5
2 names = ["Joel", "John", "Lane", "Jamie", "Freddy"]
4 for index in range(6):
----> 5 name = names[index]
6 print(index, name)
IndexError: list index out of range
Infinite loops#
Remember that with indefinite loops, the while condition must eventually become False, or the loop needs another way to exit (like break). Otherwise, it will go forever! A common error is to forget to include code in the body of the loop that changes something the condition depends on.
1n = 5
2while n > 0:
3 print(n)
4 n = n - 1
5print("Blast off!")
5
4
3
2
1
Blast off!
This works because n = n - 1 ensures the condition (n > 0) will eventually become False. What would happen if we forgot that line? The loop would run forever!
Here’s another example where the while loop condition can never become False because "John" isn’t in the list. The loop will keep going, incrementing index past the end of the list, and crash with an IndexError:
1names = ["Joel", "Jane", "Jamie"]
2to_grab = [] # output list, initialize to empty list
3
4index = 0 # set initial index to zero
5while len(to_grab) == 0: # keep going until we find something
6 print(index)
7 name = names[index] # get the name at this index
8 if name == "John": # check if is john / meets my criteria for being filtered
9 to_grab.append(name) # add the item to the output list
10 index += 1 # increment the index
11
12# print out the result
13print(to_grab)
0
1
2
3
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[62], line 7
5 while len(to_grab) == 0: # keep going until we find something
6 print(index)
----> 7 name = names[index] # get the name at this index
8 if name == "John": # check if is john / meets my criteria for being filtered
9 to_grab.append(name) # add the item to the output list
IndexError: list index out of range