For Loops
Imagine printing a greeting for 100 students. Writing 100 print() lines would be painful. A loop runs the same block of code over and over, and a for loop runs it once for each item in a collection. The collection can be a list, a string, a dictionary, or a range of numbers.
Looping over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(f"I like {fruit}")
print("Done!")Here is what each part of the for line does:
| Part | Meaning |
|---|---|
for |
Starts the loop. |
fruit |
The loop variable. Python creates it for you; you don't assign it beforehand. |
in fruits |
The collection to go through, one item at a time. |
: |
Ends the for line. Forgetting it is a SyntaxError. |
| indented lines | The body. These lines run once per item. |
Python runs this loop in three passes, one per item:
fruitis set to"apple", and the body printsI like apple.fruitis set to"banana", and the body printsI like banana.fruitis set to"cherry", and the body printsI like cherry.
There are no items left, so the loop ends and Python carries on with the first line that is not indented: print("Done!"), which runs only once. Indentation is how Python knows which lines belong to the loop. Use 4 spaces, the same as for if.
You can name the loop variable anything. Pick a name that describes one item, and name the list in the plural: for student in students, for price in prices.
Going deeper: After the loop, and empty lists optional
After the loop ends, the loop variable still holds the last item (cherry), and the list itself is unchanged. (pass is a statement that does nothing. Use it when Python needs a body but you have nothing to put there.)
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
pass # do nothing
print(fruit)
print(fruits)If the list is empty, the body runs zero times. That's not an error:
for fruit in []:
print("This never prints")
print("The loop ran 0 times")Looping over a string
A string is a sequence of characters, so a for loop gives you one character at a time, from left to right:
for letter in "Python":
print(letter)Every character counts, including spaces and punctuation. This loop counts the vowels in a sentence. len() reports 14 characters, because the two spaces, the comma and the ! count too, and the loop finds 4 vowels (i, e, e, a):
sentence = "Hi there, Sam!"
vowels = 0
for char in sentence:
if char in "aeiouAEIOU":
vowels += 1
print(len(sentence), "characters")
print(vowels, "vowels")range(): looping a number of times
range() produces a sequence of whole numbers for a loop to go through. It's how you repeat something a set number of times, or count through numbers. The simplest form takes one number, stop, and counts from 0 up to, but not including, stop:
for i in range(5):
print("Lap", i)That prints Lap 0 to Lap 4: five laps, because range(5) gives exactly 5 numbers (0, 1, 2, 3, 4). It starts at 0 for the same reason list indexes do, so range(len(my_list)) gives every valid index of a list.
The three ways to call range()
range takes up to three numbers. The ones you leave out get a default value:
| You write | start |
stop |
step |
Numbers you get |
|---|---|---|---|---|
range(5) |
0 (default) | 5 | 1 (default) | 0, 1, 2, 3, 4 |
range(2, 6) |
2 | 6 | 1 (default) | 2, 3, 4, 5 |
range(0, 20, 5) |
0 | 20 | 5 | 0, 5, 10, 15 |
range(10, 0, -2) |
10 | 0 | -2 | 10, 8, 6, 4, 2 |
startis the first number. It is included.stopis where counting ends. It is never included: the numbers go up to it but stop just before.stepis how much to add each time. A negative step counts down.
A range doesn't show its numbers when you print it (print(range(5)) just prints range(0, 5)), so wrap it in list() to see them:
# 0 up to 5; 5 is left out print(list(range(5))) # start at 1, stop before 6 print(list(range(1, 6))) # steps of 5; 20 is left out print(list(range(0, 20, 5))) # odd numbers: steps of 2 print(list(range(1, 10, 2))) # count down; 0 is left out print(list(range(10, 0, -2))) print(list(range(5, 0, -1)))
To include a number, make stop one past it. To count from 1 to 10, write range(1, 11); to count down to 1, stop at 0.
Here is range with a start in a real loop, printing part of a times table:
for n in range(1, 4):
print(f"{n} x 7 = {n * 7}")When range() gives nothing
If step can't move from start towards stop, the range is empty and the loop body never runs. There's no error, which makes this an easy bug to miss:
# 5 can't count up to 1 print(list(range(5, 1))) # 1 can't count down to 5 print(list(range(1, 5, -1))) # start and stop are the same print(list(range(3, 3)))
To count down, you need both a start bigger than the stop and a negative step.
Going deeper: Two more range() rules optional
- The numbers must be whole numbers (
int).range(0, 1, 0.5)is an error. stepcan't be0. That would never reachstop, so Python refuses with an error.
When you only want to repeat something and don't need the number, it's common to name the loop variable _, which tells readers "I'm not using this":
for _ in range(3):
print("Hip hip hooray!")Building up a result
A very common pattern: start with an empty or zero value, then update it on every pass.
prices = [4.99, 12.50, 3.25, 8.00]
total = 0
for price in prices:
total += price
print(f"Total: {total:.2f}")Here's what happens to total on each pass through the loop:
| Pass | price |
total before |
total after |
|---|---|---|---|
| 1 | 4.99 | 0 | 4.99 |
| 2 | 12.50 | 4.99 | 17.49 |
| 3 | 3.25 | 17.49 | 20.74 |
| 4 | 8.00 | 20.74 | 28.74 |
After the last pass, the loop ends and the print line runs once.
Two placement rules make this pattern work:
total = 0goes before the loop. If you put it inside, it is reset to0on every pass, and at the endtotalonly holds the last price.printgoes after the loop (not indented). Inside the loop, it would print a running total four times.
Try moving total = 0 into the loop body and run the code again to see the first mistake.
The same idea works for building a new list. Start with an empty list [] and append() to it on each pass. The original names list is left unchanged:
names = ["ada", "grace", "alan"]
capitalized = []
for name in names:
capitalized.append(name.capitalize())
print(capitalized)
print(names)Using if inside a loop
Combine loops with if to filter or count. The if is indented once because it's inside the loop, and its own body is indented twice:
scores = [45, 82, 67, 91, 38, 74]
passed = 0
for score in scores:
if score >= 50:
passed += 1
print(score, "pass")
else:
print(score, "fail")
print(f"{passed} of {len(scores)} passed")The if is checked separately on every pass: once for 45, once for 82, and so on. The final print isn't indented at all, so it runs once, after all six scores have been checked.
enumerate(): position and value together
When you need the position as well as the item, use enumerate(). On each pass it gives you a tuple (index, item), and you can unpack it into two variables right in the for line:
podium = ["Mia", "Leo", "Ava"]
# see the pairs enumerate() makes
print(list(enumerate(podium)))
for position, name in enumerate(podium, start=1):
print(f"{position}. {name}")- Counting starts at
0by default, like list indexes. start=1makes it count from 1 instead, which is better for lists people read. It only changes the numbers you get; the list itself still starts at index0.for position, name in ...unpacks each pair, sopositiongets the number andnamegets the item.
You may see loops like for i in range(len(podium)): followed by podium[i]. That works, but enumerate() is easier to read and harder to get wrong.
zip(): two lists side by side
zip() pairs up items from two (or more) lists by position: the first items together, then the second items, and so on. Each pair is a tuple you can unpack:
names = ["Ada", "Grace", "Alan"]
ages = [36, 85, 41]
# see the pairs zip() makes
print(list(zip(names, ages)))
for name, age in zip(names, ages):
print(f"{name} is {age}")If the lists have different lengths, zip() stops at the end of the shortest one. Extra items in the longer list are skipped silently, without an error:
names = ["Ada", "Grace", "Alan"]
ages = [36, 85]
# Alan has no age, so he isn't printed
for name, age in zip(names, ages):
print(name, age)Looping over a dictionary
Looping over a dictionary gives you its keys, in the order they were added. Use .values() for just the values, or .items() to get each key and value together as a pair:
stock = {"apples": 12, "pears": 0, "bananas": 7}
# the keys
for fruit in stock:
print(fruit)
# the values
for count in stock.values():
print(count)
# key and value together
for fruit, count in stock.items():
if count == 0:
print(f"{fruit}: SOLD OUT")
else:
print(f"{fruit}: {count} left")| Loop over | Each pass gives you |
|---|---|
stock |
a key, such as "apples" |
stock.values() |
a value, such as 12 |
stock.items() |
a (key, value) pair, such as ("apples", 12) |
Counting words is a classic use of a loop plus a dictionary:
text = "the cat sat on the mat the end"
counts = {}
for word in text.split():
counts[word] = counts.get(word, 0) + 1
print(counts)text.split() turns the sentence into a list of words. The key line is counts[word] = counts.get(word, 0) + 1:
counts.get(word, 0)gives the current count forword, or0if the word hasn't been seen yet. Plaincounts[word]would crash with aKeyErrorthe first time.+ 1adds this occurrence, and the result is stored back underword.
So the first "the" stores 1, the second 2, and the third 3.
break and continue
break and continue change what happens on the current pass. You almost always use them inside an if:
| Statement | What it does |
|---|---|
break |
Stops the loop immediately. Python skips any remaining items and jumps to the first line after the loop. |
continue |
Skips the rest of this pass only. The loop carries on with the next item. |
break is useful when you've found what you were looking for, or hit something that means "stop":
numbers = [3, 8, -1, 12, 5]
for n in numbers:
if n < 0:
print("Found a negative number, stopping.")
break
print(n)
print("After the loop")This prints 3 and 8, then the message. 12 and 5 are never looked at, because break ended the loop at -1.
continue is useful for skipping items you don't want to handle:
for n in range(1, 11):
if n % 3 == 0:
continue # skip multiples of 3
print(n, end=" ")For 3, 6 and 9, continue jumps straight to the next number, so print never runs for them. The output is 1 2 4 5 7 8 10. (end=" " makes print end with a space instead of a new line, so the numbers stay on one line.)
Going deeper: for ... else optional
A for loop can have an else block. It runs only when the loop finishes without hitting break. That makes it a neat fit for searches: did you find what you were looking for, or not?
numbers = [3, 7, 11, 15]
for n in numbers:
if n % 2 == 0:
print("Found an even number:", n)
break
else:
print("No even numbers in the list")Change one of the numbers to an even number and run it again. This time break happens, so the else block is skipped.
Without else, you'd need an extra variable to remember whether you found something:
numbers = [3, 7, 11, 15]
found = False
for n in numbers:
if n % 2 == 0:
found = True
break
if not found:
print("No even numbers in the list")The easiest way to read it is as "no break". The else doesn't mean "if the loop didn't run": looping over an empty list also runs the else block, because nothing called break.
Going deeper: Loops inside loops optional
A loop can contain another loop. The inner loop runs completely for each pass of the outer loop:
for row in range(1, 4):
for col in range(1, 4):
print(row * col, end="\t")
print() # new line after each rowThe outer loop runs 3 times, and each time the inner loop runs 3 times, so the inner body runs 3 × 3 = 9 times. "\t" is a tab, which lines the numbers up in columns. A break inside the inner loop only stops the inner loop; the outer loop carries on with its next pass.
Going deeper: Don't change a list while looping over it optional
Removing items from a list while a for loop is going through it makes the loop skip items. Here the second 3 survives:
numbers = [1, 3, 3, 4, 5]
for n in numbers:
if n == 3:
numbers.remove(n)
print(numbers) # one 3 was skipped!When the first 3 is removed, every later item shifts one place to the left, so the loop jumps straight past the second 3. The safe way is to build a new list with the items you want to keep:
numbers = [1, 3, 3, 4, 5]
kept = []
for n in numbers:
if n != 3:
kept.append(n)
print(kept)You'll learn a shorter way to write this in the List Comprehensions lesson.
Exercises
Exercise 1: Countdown
Use a for loop and range() to print 5, 4, 3, 2, 1 on separate lines, then Liftoff!.
# Count down from 5
range() can count down if you give it a negative step.
range(5, 0, -1) gives 5, 4, 3, 2, 1. Print Liftoff! after the loop, not inside it.
Exercise 2: Sum of even numbers
Add up all the even numbers from 1 to 100 (inclusive) and print the total, which is 2550.
total = 0
Loop over range(1, 101) so that 100 is included.
Inside the loop, add n to total only when n % 2 == 0. Print total after the loop.
Exercise 3: Longest word
Find and print the longest word in the list without using max(). It should print elephant.
words = ["cat", "elephant", "dog", "giraffe", "ant"]
Start with longest = "" and check every word in a loop.
If len(word) > len(longest), that word becomes the new longest.
Exercise 4: Shopping receipt
Print each item with its line total, then the grand total. Use .items() to loop over the dictionary.
coffee x 2 = 6.0
muffin x 3 = 7.5
juice x 1 = 4.0
Total: 17.5
prices = {"coffee": 3.0, "muffin": 2.5, "juice": 4.0}
cart = {"coffee": 2, "muffin": 3, "juice": 1}
Loop with for item, quantity in cart.items():.
Inside the loop, multiply prices[item] by quantity, print the line and add it to total. Print the total after the loop.
Summary
for item in collection:runs the indented body once for each item. The first unindented line after it runs once, when the loop is done.range(stop),range(start, stop)andrange(start, stop, step)produce whole numbers.startdefaults to0andstepto1.startis included andstopnever is, so userange(1, 11)for 1 to 10.- A negative step counts down (
range(5, 0, -1)). If the step can't reachstop, the range is empty and the loop doesn't run. - Build results with a variable set before the loop and updated inside it (
total += x, orappendto a list). enumerate()gives(index, item)pairs (addstart=1to count from 1).zip()pairs up lists and stops at the shortest.- Looping over a dictionary gives its keys. Use
.values()for values and.items()for(key, value)pairs. breakexits the loop at once, andcontinueskips to the next pass.
Know something worth teaching? Write your own tutorial with runnable examples and share it.