All 24 modules are open from the start — nothing here is
locked, and nothing costs anything. Sign in so your progress, titles and
credentials stay with you, on every device you use.
Free forever, with your Google account. No password, no payment.
Every program so far has been as long as the work it did. Three
readings meant three lines. A for loop breaks that link: you
describe the work once and Python applies it to every item, whether there are
three of them or three hundred thousand. This is the single largest jump in what
you can write.
Ready?
1
A for Loop Walks Through a Sequence
A for loop takes something with items in it and runs the same
block once per item, handing you the current one under a name you choose.
for tag in ["loops", "lists", "functions"]:
print(tag)
# loops
# lists
# functions
tag is the loop variable. It is not special
and it is not declared anywhere — Python creates it, points it at the
first item, runs the block, points it at the second, and so on until the
sequence is exhausted.
The colon and the indented block work exactly as they did with
if. Strings are sequences too, so a loop over a string walks
it one character at a time.
for letter in "PY26":
print(letter)
Name the item, not the loop
for i in tags: tells the reader nothing.
for tag in tags: tells them what the block is about, and
makes every line inside it read as a sentence. The singular of the
collection's name is nearly always the right choice.
Quick check
A loop runs over a string of five characters. How many times does its block run?
2
range() Generates Numbers to Count With
When there is nothing to walk over — you just need to do something five
times — range() produces the numbers.
Two things to hold on to. It starts at 0 when you do not
say otherwise, and it stops before the number you give
it. Both are deliberate: range(5) gives five numbers, and
range(a, b) followed by range(b, c) covers
everything from a to c with nothing repeated and nothing missed.
That exclusive end is also the source of most off-by-one bugs, and there
is no trick for it beyond checking: if you want 1 to 10 inclusive, you
write range(1, 11).
Works, reads badly
for i in range(len(tags)): print(tags[i])
Counting to an index in order to look the item up. Two steps where one will do.
Says what it means
for tag in tags: print(tag)
Ask for the items when you want the items. Use range only when you actually want the numbers.
Quick check
Which call produces the numbers 1 through 10 inclusive?
3
The Accumulator: Building an Answer Up
Most loops are not there to print. They are there to build one answer out
of many items, and they nearly all have the same three-part shape:
total = 0 # 1. start it outside the loop
for price in prices:
total = total + price # 2. update it inside
print(total) # 3. use it after
Step 1 is where the bugs live. Put total = 0inside the loop and it resets on every pass, so the answer is
always just the last item. That mistake produces a plausible-looking
number, which is what makes it worth recognising on sight.
The same shape counts things (count += 1 when a condition
holds), finds the largest (if price > biggest:), and
builds text. Change what step 2 does and you change what the loop is for.
When you need the position as well as the item,
enumerate() gives you both, and starts at 0 unless you say
otherwise:
for number, tag in enumerate(tags, start=1):
print(number, tag)
Total
total += value
Count
count += 1, inside an if
Largest
compare, then replace when bigger
Position too
enumerate(items, start=1)
Quick check
total = 0 is written inside the loop body rather than above it. What does the loop print at the end?
4
Leaving Early, and Skipping a Pass
Two keywords change how a loop runs.
break abandons the loop entirely. The
classic use is a search: once you have found what you were looking for,
there is no reason to keep going.
for check in checks:
if check == "FAIL":
first_failure = check
break
continue skips the rest of this pass and
goes straight to the next item. It is how you filter out the rows you do
not care about without wrapping the whole body in an if:
for line in lines:
if not line.strip():
continue # blank line, nothing to do
process(line)
Both are worth using sparingly and both are worth using. A loop with three
breaks in it is hard to follow; a loop that keeps scanning a
million rows after it has its answer is just slow.
Nothing here can hang
A for loop over a finite sequence always ends — that is the
difference between it and the while loop in the next module.
If you do manage to write something that runs forever, the lab stops it
after ten seconds and hands you the editor back.
Reach for for when you know what you are walking over, and
while when you only know when to stop.
Quick check
What is the difference between break and continue?
0 of 9 completed
Real Python runs right here in your browser — nothing to install, nothing
sent to a server. The interpreter downloads once the first time you press
Run, then stays cached.
01
To do
A for loop runs its block once for every item in a sequence,
handing you the current item under a name you pick.
for letter in "AB": print(letter)
A string is a sequence of characters, so this prints A and
then B. The colon and the indented block work exactly as they
did with if.
Your task: print each character of code on
its own line:
P Y 2 6
your_code.py
PythonCtrl↵ to run
Hint
for letter in code: on one line, then print(letter) indented underneath. You never say how many characters there are — the loop works that out.
Output
02
To do
split() hands back a list, and a list is a sequence, so a
loop walks it the same way it walks a string.
The work inside the block is whatever you would have done to one item —
the loop is what makes it happen to all of them.
Your task: the tags below arrive with inconsistent
spacing. Split them on the comma, then print each one stripped and
title-cased, one per line:
Loops Lists Functions
your_code.py
PythonCtrl↵ to run
Hint
for tag in tags: then print(tag.strip().title()) inside. Both methods chain onto the item, exactly as they would if there were only one.
Output
03
To do
range() produces numbers. It starts at 0 unless told
otherwise, and it always stops before the number you give it.
The exclusive end is deliberate — range(5) gives you five
numbers — and it is also where nearly every off-by-one bug comes from.
Your task: two loops. The first prints 1 to 5 inclusive,
one per line. The second counts back down from 3 to 1. Eight lines
altogether:
1 2 3 4 5 3 2 1
your_code.py
PythonCtrl↵ to run
Hint
range(1, 6) for the first — the end is exclusive, so 6 gets you 5. For the countdown, range(3, 0, -1): start at 3, stop before 0, step backwards by one.
Output
04
To do
The most useful loop does not print. It builds an answer, and it always
has the same three parts:
total = 0 # start it OUTSIDE the loop for price in prices: total += price # update it inside print(total) # use it after
Your task: four line items arrive as one comma-separated
string. Total them into total, then print the total and the
average to two decimal places:
Total: 3040 Average: 760.00
Remember that split() hands back text. Each piece needs
converting before it can be added to anything.
your_code.py
PythonCtrl↵ to run
Hint
for amount in amounts: then total += int(amount) inside. The int() matters — adding the text "1250" to a number raises a TypeError.
Output
05
To do
Change what the accumulator does and the same shape counts instead of
totalling: start at zero, add one whenever a condition holds, read it
afterwards.
count = 0 for line in lines: if "ERROR" in line: count += 1
splitlines() cuts a multi-line string into a list of lines,
which is how a block of log text becomes something a loop can walk.
Your task: count how many of the five log lines are
errors, and print:
Errors: 2 of 5
your_code.py
PythonCtrl↵ to run
Hint
for line in lines: then an if inside it — "ERROR" in line is True when the text appears anywhere in that line. Increment with errors += 1.
Output
06
To do
Sometimes the block needs to know where it is, not just what it
has. enumerate() hands back both, as two names on the
for line:
for number, tag in enumerate(tags, start=1): print(number, tag)
Without start=1 the numbering begins at 0, which is right for
indexes and wrong for anything a person reads.
This is what replaces for i in range(len(tags)) — that
version counts to a number in order to look the item up, which is two
steps where one will do.
Your task: print a numbered menu:
1. loops 2. lists 3. functions
your_code.py
PythonCtrl↵ to run
Hint
for number, tag in enumerate(tags, start=1): then print(f"{number}. {tag}") inside — note the full stop and the space in the output.
Output
07
To do
break abandons the loop immediately. The classic use is a
search: once the thing is found, every remaining pass is wasted work.
for check in checks: if check == "FAIL": break
Your task: find the position of the first failing
check, counting from 1, and stop looking as soon as you have it. There are
two failures in the list; only the first one matters.
First failure at check 3
your_code.py
PythonCtrl↵ to run
Hint
Loop with enumerate(checks, start=1) so you have the position, set it when the check is "FAIL", then break out of the loop.
Output
08
To do
continue abandons the current pass and moves to the next
item. It is how you drop the rows that are not your problem without
wrapping the whole body in an if:
for line in lines: if not line.strip(): continue print(line)
With one filter the two shapes are equivalent. With three, the
continue version stays flat while the nested version marches
off the right of the screen.
Your task: print only the real settings from this config,
skipping blank lines and anything starting with #. Strip each
line before printing it:
port=8080 debug=false timeout=30
your_code.py
PythonCtrl↵ to run
Hint
Strip the line into a variable first. Then: if not line: continue, and if line.startswith("#"): continue. Whatever survives both is a setting.
Output
09
To do
This loop reports a total of 79 for three scores that add up
to 259. It raises no error, and the number it prints is a
perfectly believable score.
The accumulator is being created inside the loop, so every pass
throws away everything before it. What survives to the print is the last
item alone.
Your task: move one line, and print the real total:
259
your_code.py
PythonCtrl↵ to run
Hint
total = 0 belongs above the loop, not inside it. Start it once, add to it many times.
Output
The log summariser
To do
A service wrote this log overnight and somebody has to say what happened in
it before standup. Walk it once and produce the summary.
Each real line is a date, a level and a message, padded so the levels line
up on screen. Blank lines and lines starting with # are not log
entries and must be skipped entirely — they count towards nothing.
Work these out:
total_lines — how many real entries there are
infos, warnings, errors — how many of each level
first_error — the message of the first ERROR entry, and nothing after it should change this
error_rate — errors as a percentage of the real entries
Then print exactly six lines:
Lines: 6 INFO: 3 WARN: 1 ERROR: 2 First error: db timeout after 30s Error rate: 33.3%
your_code.py
PythonCtrl↵ to run
Hint
Loop over log.splitlines(). Strip each line, then continue past the blanks and the # lines. What is left splits with maxsplit=2 into date, level and message. Count with += 1 inside an if/elif chain, and take the first error only when first_error is still empty — or set it and break out of a second, separate search. The rate is errors / total_lines * 100, shown with {error_rate:.1f}.