for Loops

Python · Lesson 16 of 56 6 min read Updated

Most of the time you loop because you have a collection of things and want to do something with each one: print every student's name, add up every price, check every line of a file. Python's for loop is made for exactly this, and it is the loop you will write most often.

In this lesson you will loop over lists and strings, count with range(), get positions with enumerate(), walk two lists together with zip(), control loops with break, continue and else, and nest one loop inside another.

Looping over a collection

A for loop takes each item from a collection, one at a time, stores it in a loop variable, and runs the block for that item.

PythonRun it yourself
students = ["Aarav", "Priya", "Kabir", "Meera"]

for name in students:
    print(f"Good morning, {name}!")

for letter in "Pune":
    print(letter, end=" ")
Output
Good morning, Aarav!
Good morning, Priya!
Good morning, Kabir!
Good morning, Meera!
P u n e 

Read the first loop as "for each name in students". You choose the loop variable's name, so pick one that describes a single item: name for students, price for prices. A string is a collection of characters, so the second loop visits each letter.

Any iterable works in a for loop. An iterable is anything that can hand out its items one by one: lists, strings, tuples, sets, dictionaries, files and more. You will learn how this works in Iterators & Generators.

Counting with range()

range() produces a sequence of whole numbers, which is how you repeat something a set number of times.

CallNumbers produced
range(5)0, 1, 2, 3, 4
range(1, 6)1, 2, 3, 4, 5
range(10, 0, -2)10, 8, 6, 4, 2

The pattern is range(start, stop, step). The stop number is not included, just like slicing a string. start defaults to 0 and step to 1.

PythonRun it yourself
number = 7

for i in range(1, 11):
    print(f"{number} x {i:>2} = {number * i:>2}")
Output
7 x  1 =  7
7 x  2 = 14
7 x  3 = 21
7 x  4 = 28
7 x  5 = 35
7 x  6 = 42
7 x  7 = 49
7 x  8 = 56
7 x  9 = 63
7 x 10 = 70

To get 1 to 10 you write range(1, 11). If you only need to repeat something and never use the number, name the variable _, a convention that means "I do not need this": for _ in range(3):.

Position and item with enumerate()

When you need the item and its position, wrap the collection in enumerate(). It gives you pairs of (position, item), and start=1 makes the count begin at 1, which suits lists shown to people.

PythonRun it yourself
toppers = ["Ananya", "Rohan", "Isha"]

for rank, name in enumerate(toppers, start=1):
    print(f"{rank}. {name}")
Output
1. Ananya
2. Rohan
3. Isha

You may see for i in range(len(toppers)): followed by toppers[i] in older code or code ported from other languages. It works, but enumerate() is clearer and is the Python way.

Two lists together with zip()

zip() walks through two or more collections side by side, giving you one item from each on every pass.

PythonRun it yourself
items = ["Notebook", "Pen", "Geometry box"]
prices = [60, 15, 120]
quantities = [4, 3, 1]

total = 0
for item, price, qty in zip(items, prices, quantities, strict=True):
    line = price * qty
    total += line
    print(f"{item:<13} {qty} x ₹{price:<4} = ₹{line}")

print(f"Total: ₹{total}")
Output
Notebook      4 x ₹60   = ₹240
Pen           3 x ₹15   = ₹45
Geometry box  1 x ₹120  = ₹120
Total: ₹405

By default, zip() stops quietly at the end of the shortest list, which can hide a missing value. Passing strict=True makes it raise a ValueError if the lists have different lengths, so a mistake is caught straight away.

break, continue and else

These work in for loops exactly as in while Loops: break leaves the loop, continue skips to the next item, and the loop's else block runs only if the loop finished without break.

PythonRun it yourself
trains = ["12951 Rajdhani", "12002 Shatabdi", "CANCELLED", "22439 Vande Bharat"]
wanted = "12002"

for train in trains:
    if train == "CANCELLED":
        continue
    if train.startswith(wanted):
        print("Found:", train)
        break
else:
    print("Train", wanted, "is not running today")
Output
Found: 12002 Shatabdi

Change wanted to "19019" and run it again. No break happens, so the else block reports that the train was not found.

Nested loops

A loop inside a loop runs the inner loop completely for every single pass of the outer loop. This is how you work with grids, tables and pairs.

PythonRun it yourself
rows = ["A", "B", "C"]

for row in rows:
    for seat in range(1, 5):
        print(f"{row}{seat}", end=" ")
    print()
Output
A1 A2 A3 A4 
B1 B2 B3 B4 
C1 C2 C3 C4 

The inner loop prints four seats on one line (end=" " keeps them together), and the empty print() after it ends the line. Three rows times four seats gives twelve seats. Be careful with nesting: a loop inside a loop over 10,000 items each is 100 million iterations.

Python also has handy helpers you can loop over: reversed(items) goes backwards and sorted(items) goes in order, both without changing the original list. Looping over dictionaries is covered in Dictionaries.

Common mistakes

  • Changing a list while looping over it. Removing items from the list you are looping over makes Python skip items. Loop over a copy (for m in marks[:]:) or build a new list instead, as you will learn in Comprehensions.
  • Forgetting that range() stops early. range(1, 10) ends at 9. Write range(1, 11) for 1 to 10.
  • Using range(len(...)) when you need items. Loop over the list directly, and use enumerate() when you also need the position.
  • Ignoring different lengths in zip(). Without strict=True, extra items are silently dropped.

Key points

  • for item in collection: runs the block once for each item in any iterable.
  • range(start, stop, step) produces numbers; stop is not included.
  • enumerate(items, start=1) gives position and item together.
  • zip(a, b, strict=True) walks several collections in step and catches length mismatches.
  • break, continue and the loop else work the same way as in while loops.
  • Never add or remove items in a list while looping over that same list.

Exercise

Kabir's batting scores over six matches are [45, 0, 102, 33, 8, 76]. Using one for loop with enumerate(), print each match number and score, add up the total, count the half-centuries (50 or more), and skip printing for ducks (a score of 0) with continue. After the loop, print the total and the number of half-centuries.

Show solution
Python
scores = [45, 0, 102, 33, 8, 76]
total = 0
fifties = 0

for match_no, runs in enumerate(scores, start=1):
    total += runs
    if runs >= 50:
        fifties += 1
    if runs == 0:
        continue
    print(f"Match {match_no}: {runs}")

print("Total runs:", total)
print("Fifty-plus scores:", fifties)
Output
Match 1: 45
Match 3: 102
Match 4: 33
Match 5: 8
Match 6: 76
Total runs: 264
Fifty-plus scores: 2

The total and count are updated before continue, so the duck still counts towards the totals but is not printed. enumerate() with start=1 gives match numbers that start at 1.

Quick Quiz

1. Which numbers does range(2, 8, 2) produce?

2. What does enumerate(["a", "b"], start=1) give in a loop?

3. What does zip([1, 2, 3], ["x", "y"]) do with the extra 3?

4. When does the else block of a for loop run?

Learn Python with a mentor

Free lessons get you started. Our Python Programming training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call