Chapter 6 — Loops II: for, range & Nested Loops
The module's primary reading · ~35–45 minutes · Objective 2 (use variables and the core types int, float, str, and bool; evaluate expressions with correct operator precedence, including true division, floor division, and remainder; convert between types; and read input and build output with indexing, slicing, and f-strings) and Objective 4 (write while loops and for loops over range(), using counters, accumulators, nested loops, and break, and recognize and repair an infinite loop and an off-by-one error) · Read it first this week; the lecture, the slides, the tutorial, the quiz, and the Coding Lab all build on it.
In plain language
Last week your programs learned to repeat with a while loop, and you had to set up a counter and remember to move it every time. This week you meet a loop that does that bookkeeping for you. A for loop (a loop that takes the items of a sequence one at a time) walks through a string letter by letter, or through a range (a row of whole numbers Python makes on request) number by number. You will learn exactly where a range stops, how to total numbers with it, and how to put one loop inside another to print grids and tables. As always, you predict what each program prints and then run it to check.
Why you already care about this
A coach hands you a card that says "7 laps." You do not keep a separate tally in your head and test it before every lap; you run lap 1, lap 2, and so on up to lap 7, and then you stop. The number of repeats was known before you started. Now picture a cinema usher who walks row 1, checks seats 1 to 10, then walks row 2 and checks seats 1 to 10 again, and so on for 12 rows. That is a loop inside a loop, and 12 rows of 10 seats is 120 checks.
Week 5's while loop is the right tool when you do not know how many passes you need, such as "keep reading until the goal is met." This week's for loop is the right tool when you do know, such as "do this for each of the 7 days" or "for each letter of this word." It sets the counter, moves it, and stops it for you, so a whole family of off-by-one and infinite-loop bugs never gets a chance to start.
This chapter shows you the for loop over a string and over range(), the three forms of range(), totals built with a for loop, rows of output controlled with end, and nested loops. The reading log from earlier weeks runs a full week of reading this time.
By the end of this chapter you will be able to:
- write a
forloop over a string and say what the loop variable holds on each pass; - predict exactly which numbers
range(stop),range(start, stop), andrange(start, stop, step)produce, and check withlist(); - total numbers with a
forloop and chooserange(1, n + 1)when you mean 1 to n; - keep output on one line with
endand finish the line with a bareprint(); - trace a nested loop and count its lines of output before you run it;
- check a chatbot's range by running the loop yourself.
Carry this line with you: a range stops before its stop — never on it.
Where we are
- Already covered: Week 1 taught you to predict, run, and read an error from its last line; Weeks 2 and 3 gave you variables, arithmetic, strings, indexing, and
len(); Week 4 taught conditions; Week 5 taught thewhileloop, counters, accumulators, and the off-by-one error. - This week: the
forloop over a string and overrange(), the exclusive stop, totals withfor,endand a bareprint(), and nested loops. - How they connect: a
forloop is a counter loop with the counting built in, and its stop follows the same half-open rule as Week 3's slices. - Where we're going next: Week 7, Functions, packages a loop like these into a named piece of work you can reuse.
Three earlier ideas carry most of the weight. From Week 3, a slice such as "PYTHON"[1:4] keeps its start and drops its stop, so it printed YTH; a range treats its stop the same way. From Week 5, an accumulator starts at a value before the loop and += adds to it on every pass. Also from Week 5, a loop that runs one pass too few or too many has an off-by-one error. Spend extra time on Sections 3 and 6; Week 9's lists and Week 13's algorithms lean on both.
1. Walk a string: the for loop hands you each character
A teacher calling roll does not need to count names on a sheet. The teacher reads the first name, then the next, then the next, until the sheet runs out. Nobody has to say "now move to name number 4." Moving to the next one is built into reading a list from top to bottom.
A for loop is a statement that runs its indented body once for each item of a sequence, such as each character of a string or each number of a range, handing the current item to a name before every pass.
The loop variable is the name written between for and in, which Python sets to the next item of the sequence at the start of every pass, so it never needs an update line of its own.
Worked example — spelling out a word. Predict how many lines this prints, and what each one says.
for letter in "river":
print(letter)
Output
r
i
v
e
r
Here is the trace, pass by pass.
- Pass 1:
lettergets "r", and the body prints r. - Pass 2:
lettergets "i", and the body prints i. - Passes 3, 4, and 5 hand over v, e, and r the same way.
- The string has no sixth character, so the loop ends.
In words: a five-character string gave five passes and five lines. There was no counter to set up, no condition to test, and no update to forget.
Here is the same idea in a different setting, a sign-up page counting the characters of an illustrative password with a loop instead of len().
password = "maple77"
count = 0
for character in password:
count += 1
print(count)
print(len(password))
Output
7
7
The loop added 1 for each of the 7 characters, so count ended at 7, the same number len() gives. In words: a for loop over a string makes exactly one pass per character, no more and no fewer.
Misconception check. Students often think a
forloop needs a counter and an update line the way Week 5'swhileloop did, so they add an update line such asi += 1inside a loop over a range. That is tempting, because last week every loop moved its own counter. But theforline itself sets the loop variable to the next item on every pass, so changing it inside the body does not move the loop along: a loop overrange(5)still makes five passes. The cure: let theforline do the moving, and use the loop variable only to read the current item.
In practice (your tools). Paste the two-line "river" loop into Python Tutor at pythontutor.com and click Next one step at a time. Watch letter change from r to i to v to e to r in the variables panel, and watch the output grow one line per pass. The Python Tutorial's section 4.2, for Statements, on docs.python.org, is the official description.
Memory hook: for hands you each item; you never move it yourself.
2. Repeat a known number of times: for and range
A pill organizer has seven boxes labelled for seven days. You do not test a condition before each box; you fill box 1, box 2, and so on through box 7, and then you are done. When you know the number of repeats before you start, a range() gives a for loop that number of passes.
range() is the built-in that produces whole numbers from a start, 0 when none is given, up to but not including a stop, moving by a step that is 1 when none is given.
Worked example — a week of the reading log. The reading log starts with 45 pages read and adds 30 pages a day. This week's stage runs it for a full week. Predict the first line, the last line, and how many lines print.
pages_read = 45
for day in range(1, 8):
pages_read = pages_read + 30
print(day, pages_read)
print(pages_read)
Output
1 75
2 105
3 135
4 165
5 195
6 225
7 255
255
Here is the reasoning.
range(1, 8)holds 1 through 7, which is 8 − 1 = 7 values, so the body runs 7 times.- On pass 1,
dayis 1 andpages_readbecomes 45 + 30 = 75, so the line reads 1 75. - Each pass adds 30 more. After 7 passes the total is 45 + 7 × 30 = 45 + 210 = 255, so the last line inside the loop reads 7 255.
- The final
print(pages_read)is not indented, so it runs once, after the loop, and prints 255.
In words: the loop made one pass per day, seven days, and finished with 255 pages read. The stop 8 is never a day; it only marks where the range ends.
Compare that with Week 5. The while loop kept adding until the goal of 300 was passed, and it needed 9 days and overshot to 315. This for loop does not look at the goal at all. It runs exactly 7 passes because the range told it to. Use while when the stopping point depends on what happens inside the loop, and for when you know the count ahead of time.
Here is a second setting, an illustrative fitness band that adds 4000 steps on each of 5 days.
steps = 0
for day in range(1, 6):
steps += 4000
print(day, steps)
Output
1 4000
2 8000
3 12000
4 16000
5 20000
range(1, 6) gave 6 − 1 = 5 days, and 5 × 4000 = 20000 steps by the last line.
Misconception check. Students often think
range(1, 8)runs 8 times, because the number 8 is written right there. That is tempting, because people count "1 to 8" out loud. But the stop is never produced, sorange(1, 8)gives 1 through 7, seven values. The cure: count stop minus start, 8 − 1 = 7, and expect exactly that many passes.
In practice (your tools). Open your free browser Python runner, type the reading-log program, and press Run. Then change range(1, 8) to range(1, 4) and predict the new last line before you run it again. The Python Tutorial's section 4.3, The range() Function, on docs.python.org, describes every form of range().
Memory hook: with a step of 1, stop minus start is the number of passes.
3. Read a range: three forms and the exclusive stop
A ticket roll at a deli counter starts at some number, hands out one ticket at a time, and the clerk says "we stop before ticket 50." A range is that roll of tickets, and you choose where it starts, where it stops, and how big each jump is.
range() comes in three forms.
range(stop)starts at 0 and goes up by 1.range(start, stop)starts where you say and goes up by 1.range(start, stop, step)starts where you say and goes up bystep.
In every form the exclusive stop applies: the rule that the stop number of a range is never one of the numbers it produces, so range(1, 5) gives 1, 2, 3, and 4 but not 5.
A range does not print its numbers on its own. Wrapping it in list() is the trick for seeing what it holds; lists themselves come later, in Week 9.
Worked example — three forms side by side. Predict each line before you read the output.
print(list(range(5)))
print(list(range(0, 10, 3)))
print(list(range(0, 10, 2)))
print(len(range(1, 10, 3)))
Output
[0, 1, 2, 3, 4]
[0, 3, 6, 9]
[0, 2, 4, 6, 8]
3
Line by line:
range(5)starts at 0 and stops before 5: five values, 0 through 4.range(0, 10, 3)starts at 0 and jumps by 3: 0, 3, 6, 9. The next jump would reach 12, which is past the stop.range(0, 10, 2)jumps by 2: 0, 2, 4, 6, 8. It does not include 10, because 10 is the stop.range(1, 10, 3)holds 1, 4, and 7, solen()counts 3 values.
Now the pinned loop over range(1, 5). The end=" " part keeps the numbers on one line, which Section 5 explains.
for i in range(1, 5):
print(i, end=" ")
print()
Output
1 2 3 4
Four numbers, 1 through 4. The 5 never appears, because it is the stop.
Here is a step in an everyday setting, an illustrative temperature table that runs from 0 to 30 degrees Celsius in jumps of 10.
for celsius in range(0, 31, 10):
print(celsius, celsius * 9 / 5 + 32)
Output
0 32.0
10 50.0
20 68.0
30 86.0
To include 30, the stop has to be past it, so the program uses 31. For the last row, 30 × 9 = 270, 270 ⁄ 5 = 54.0, and 54.0 + 32 = 86.0. The .0 appears because / always gives a float, as Week 2 showed.
Misconception check. Students often think
range(5)starts at 1, and thatrange(0, 10, 2)includes 10. Both are tempting, because everyday counting starts at 1 and "0 to 10" sounds like it should end on 10. But a range with no start begins at 0, and the stop is never produced. The cure: when a range surprises you, printlist(range(...))and read the numbers it holds.
In practice (your tools). In your browser runner, type print(list(range(0, 10, 3))) and run it. Then change the step to 4 and to 5, predicting each time. The Python Tutorial's section 4.3 shows list(range(...)) used the same way.
Memory hook: no start means 0; the stop is never in the range.
4. Total with for: range(n) versus range(1, n + 1)
A cashier totalling a receipt keeps a running sum: it starts at zero, and each item gets added. Week 5 called that running sum an accumulator, and a for loop is a natural home for one, because it hands you each number to add.
Worked example — the total of 1 to 5. Predict both totals before you read the output.
total = 0
for n in range(1, 6):
total += n
print(total)
total = 0
for n in range(1, 5):
total += n
print(total)
Output
15
10
The first loop runs over 1, 2, 3, 4, and 5, so total collects 1 + 2 + 3 + 4 + 5 = 15. The second loop stops before 5, so it adds only 1 + 2 + 3 + 4 = 10. In words: the second program ran with no error and printed the wrong total, which makes it a logic error, and its cause is an off-by-one in the range.
The fix depends on where you want to count from. With n = 5:
n = 5
for i in range(n):
print(i, end=" ")
print()
for i in range(1, n + 1):
print(i, end=" ")
print()
Output
0 1 2 3 4
1 2 3 4 5
range(n) gives n values starting at 0. range(1, n + 1) gives n values starting at 1, because the stop n + 1 is left out, so the last value is n. Both run 5 passes; they hand the loop different numbers.
Here is an illustrative savings plan in another setting: in the first saving week you put away 2 dollars, in the second 4 dollars, and so on, twice the week's number each time, for 6 weeks.
saved = 0
for week in range(1, 7):
saved += 2 * week
print(saved)
Output
42
The saving weeks run 1 through 6, so the amounts are 2, 4, 6, 8, 10, and 12 dollars, and 2 + 4 + 6 + 8 + 10 + 12 = 42. Using range(1, 7) for "6 weeks counted from 1" is the range(1, n + 1) pattern with n = 6.
Misconception check. Students often think
range(1, n)covers 1 to n, so they writerange(1, 5)to sum 1 to 5. That is tempting, because both numbers you meant are on the screen. But the stop is left out, so the loop ends at 4 and the total comes out 10 instead of 15. The cure: when you mean "1 to n," writerange(1, n + 1), and check the last value withlist().
In practice (your tools). Type the two total loops into Python Tutor at pythontutor.com and step through them. Watch total grow by the value of n on each pass, and watch which value of n is the last one in each loop.
Memory hook: to end on n, stop at n + 1.
5. End a line where you choose: end and a bare print
A printer that types one character and then feeds a fresh sheet of paper would waste a lot of paper. You want it to stay on the same line until a row is finished. Normally print() adds a line break after its output. You can change that.
end= is the setting inside the parentheses of print that replaces the line break print normally adds after its output, so end=" " leaves a space and end="" leaves nothing.
Worked example — a row of stars. Predict whether the stars land on one line or four.
for i in range(4):
print("*", end="")
print()
Output
****
Each pass prints one star and, because of end="", no line break. After four passes the row holds four stars. The bare print() after the loop prints nothing but a line break, which finishes the row.
The bare print() matters. Here is an illustrative download progress bar, with and without it.
for tick in range(5):
print("#", end="")
print()
print("done")
Output
#####
done
for tick in range(5):
print("#", end="")
print("done")
Output
#####done
In the second program nothing finished the row, so done landed on the same line as the bar. In words: end decides what follows each print, and a bare print() is how you close a row on purpose.
Misconception check. Students often think
end=""belongs after the loop, or that a bareprint()does nothing. Both are tempting, because aprint()with empty parentheses looks like it has nothing to say. Butendchanges each print inside the loop, and the bareprint()prints the line break that ends the row. The cure: read each row of output as "what printed, then what ended it."
In practice (your tools). In your browser runner, run the star row, then delete the bare print() and add print("next") at the end. Predict where next lands before you run it. The Standard Library reference, built-in functions, under print(), on docs.python.org, describes end.
Memory hook: end says what follows; a bare print() ends the row.
6. Nest one loop inside another: grids, tables, and counting lines
Two coins are flipped for a game. To list every outcome, you take the first coin's side, and for that side you run through both sides of the second coin; then you take the first coin's other side and run through the second coin again. That is a loop inside a loop.
A nested loop is a loop written inside the body of another loop, so the whole inner loop runs from start to finish on every pass of the loop around it.
The outer loop is, in a nested loop, the loop whose body holds the other loop; each of its passes starts the inner loop again from the beginning.
The inner loop is, in a nested loop, the loop that sits inside the body of the other loop and runs all of its passes before the loop around it moves on.
Worked example — the 2 by 3 nested loop. Predict how many lines print before you read the output.
for i in range(2):
for j in range(3):
print(i, j)
Output
0 0
0 1
0 2
1 0
1 1
1 2
The outer loop makes 2 passes. On each one, the inner loop makes all 3 of its passes. That is 2 × 3 = 6 lines. Notice that i stays at 0 while j runs 0, 1, 2, and only then does i move to 1.
Worked example — the star grid. Now the inner loop prints a row of stars, and a bare print() sits in the outer loop's body, after the inner loop.
for row in range(3):
for col in range(4):
print("*", end="")
print()
Output
****
****
****
The indentation tells the story. The bare print() is indented four spaces, so it belongs to the outer loop and runs once per row, after the inner loop finishes. Three rows of four stars is 3 × 4 = 12 stars.
Worked example — the 3 by 3 table. The pinned table prints i * j for each pair.
for i in range(1, 4):
for j in range(1, 4):
print(i * j, end=" ")
print()
Output
1 2 3
2 4 6
3 6 9
Row 1 multiplies 1 by 1, 2, and 3. Row 2 multiplies 2 by each, and row 3 ends with 3 × 3 = 9.
Here are the two coins, an illustrative game of chance, with a nested loop over strings.
for first in "HT":
for second in "HT":
print(first + second)
Output
HH
HT
TH
TT
Two sides on the outside, two on the inside: 2 × 2 = 4 outcomes, one per line.
Misconception check. Students often think the inner loop runs once and then the outer loop carries on. That is tempting, because the inner loop is written only once on the page. But the inner loop sits in the outer loop's body, so it starts over on every outer pass. You can watch it happen:
for i in range(2):
print("outer", i)
for j in range(3):
print(" inner", j)
Output
outer 0
inner 0
inner 1
inner 2
outer 1
inner 0
inner 1
inner 2
The cure: count lines as outer passes times inner passes, and trace one full outer pass before you move to the next.
In practice (your tools). Paste the 2 by 3 nested loop into Python Tutor at pythontutor.com and click Next until the output panel shows six lines. Watch j reset to 0 each time i changes. Then move the bare print() in the star grid four spaces to the left, out of the outer loop, and predict the output before you run it in your browser runner.
Memory hook: the inner loop finishes every time; lines = outer × inner.
7. Check a chatbot's range by running it
A friend gives you directions and says "take the third exit." You count the exits as you pass them rather than trusting a guess about where it is. A chatbot's code deserves the same care: you count by running it.
Worked example — a chatbot's "1 to 5." Suppose you ask a chatbot for a loop that prints the numbers 1 to 5 on one line, and it answers with this code, saying it prints 1 2 3 4 5.
for i in range(1, 5):
print(i, end=" ")
print()
Output
1 2 3 4
The run printed four numbers, not five. The chatbot wrote range(1, 5) for "1 to 5", the classic range slip, because it treated the stop as the last number. The fix is one character:
for i in range(1, 6):
print(i, end=" ")
print()
Output
1 2 3 4 5
In words: the run caught a claim that sounded right, and the cure was the range(1, n + 1) pattern from Section 4.
Misconception check. Students often think a chatbot's loop is safe because its explanation names the right numbers. That is tempting, because "range from 1 to 5" reads correctly in English. But a chatbot predicts likely text; it does not run your program. The cure: paste the code into your runner, compare the output with what the chatbot said it would print, and trust the run.
In practice (your tools). Ask your approved chatbot for a loop that prints every even number from 2 to 10. Before you run its code, write down the numbers you expect; then run it in your browser runner and check whether 10 appears. The Python Tutorial's section 4.3 on docs.python.org is the place to confirm how the stop works.
Honesty note: this chapter covers only the loop tools Week 6 needs. The words break and continue also work inside a for loop; you met break with while in Week 5, and more use of both comes later. Lists, which a for loop walks the same way it walks a string, come in Week 9.
Caveat: every range in this chapter counts upward. A range can also count down with a negative step, and you can check one the same way, with list(), before you trust it.
Pause and choose one of these to think about before you read on:
- (a) Think of a routine you repeat a fixed number of times, such as reps at the gym or stops on a bus route. Would a
forloop or awhileloop describe it better, and why? - (b) Think of a grid you use, such as a calendar page, a seating chart, or a spreadsheet. Which part would be the outer loop and which the inner loop, and how many cells would the loops visit?
Memory hook: trust the run, not the range someone describes.
Key terms
- for loop — A statement that runs its indented body once for each item of a sequence, such as each character of a string or each number of a range, handing the current item to a name before every pass. (first used: 1. Walk a string: the for loop hands you each character)
- loop variable — The name written between for and in, which Python sets to the next item of the sequence at the start of every pass, so it never needs an update line of its own. (first used: 1. Walk a string: the for loop hands you each character)
- range() — The built-in that produces whole numbers from a start, 0 when none is given, up to but not including a stop, moving by a step that is 1 when none is given. (first used: 2. Repeat a known number of times: for and range)
- exclusive stop — The rule that the stop number of a range is never one of the numbers it produces, so range(1, 5) gives 1, 2, 3, and 4 but not 5. (first used: 3. Read a range: three forms and the exclusive stop)
- end= — The setting inside the parentheses of print that replaces the line break print normally adds after its output, so end=" " leaves a space and end="" leaves nothing. (first used: 5. End a line where you choose: end and a bare print)
- nested loop — A loop written inside the body of another loop, so the whole inner loop runs from start to finish on every pass of the loop around it. (first used: 6. Nest one loop inside another: grids, tables, and counting lines)
- outer loop — In a nested loop, the loop whose body holds the other loop; each of its passes starts the inner loop again from the beginning. (first used: 6. Nest one loop inside another: grids, tables, and counting lines)
- inner loop — In a nested loop, the loop that sits inside the body of the other loop and runs all of its passes before the loop around it moves on. (first used: 6. Nest one loop inside another: grids, tables, and counting lines)
Summary
- A
forloop makes one pass per item and sets its loop variable for you. - Use
forwhen you know the count; usewhilewhen a condition decides. range(stop)starts at 0; the stop is never produced.- Stop minus start, with a step of 1, is the number of passes.
list(range(...))shows what a range holds.- To count 1 to n, write
range(1, n + 1). end=""keeps output on one line; a bareprint()ends the row.- A nested loop prints outer passes times inner passes lines.
Check your understanding
Work these before looking at the answers — they are ungraded, and the answers (with reasoning) are at the bottom of the page.
- A bakery app labels its trays with
list(range(2, 11, 4))(an illustrative example). Predict what it prints, and say why 10 is or is not included. - A club charges 10 dollars a month in an illustrative example, and a program totals the first three months with
for n in range(1, 4):andtotal += n * 10, starting fromtotal = 0. Predict the total, then say what the total would be if someone wroterange(1, 5)instead. - A program has an outer loop over
range(4)and an inner loop overrange(5), and the inner body adds 1 tolines, which starts at 0. Predict the value oflinesafter both loops end. - A spelling app runs
for letter in "kayak":withprint(letter)in the body. How many lines print, and why does the loop not need a counter?
Answers (worked reasoning — read after attempting)
- It prints [2, 6, 10]. The range starts at 2 and jumps by 4: 2, then 2 + 4 = 6, then 6 + 4 = 10. The stop is 11, and 10 is below 11, so 10 is included. The next jump would be 14, which is past the stop.
- The range holds 1, 2, and 3, so the total is 10 + 20 + 30 = 60. With
range(1, 5)the loop also adds 4 × 10 = 40, so the total becomes 100, the cost of four months. Each extra value in a range is one extra pass, so a change of one in the stop changes the total by a whole month. linesends at 20. The outer loop makes 4 passes, and each one runs the inner loop's 5 passes in full, so the body runs 4 × 5 = 20 times. This is the "inner loop runs once" trap: the inner loop restarts on every outer pass.- Five lines print: k, a, y, a, k. The word has 5 characters, and a
forloop makes one pass per character. Theforline setsletterto the next character on every pass, so there is no counter to start, move, or forget.
Next up in this module: the Readings & Resources page, the lecture tutorial, the practice set, and the quiz — and next week, Functions bring a new mechanism: a named piece of work, built from loops like these, that you can call again and again.
Built with The Course Maker · samples.thecoursemaker.com