Python with AI Tutorial · Chapter 19 of 48
A Python for loop walks through the items of a sequence one at a time and runs a block of code for each one. It works on lists, tuples, strings, dictionaries and anything else that is iterable. Combined with range(), enumerate() and zip(), the for loop is the workhorse of everyday data processing in Python.
Looping through a list
Write for, a variable name, in and the sequence. The variable takes each value in turn.
regions = ["North", "South", "East", "West"]
for region in regions:
print(f"Generating report for {region}")
Output: Generating report for North Generating report for South Generating report for East Generating report for West
Unlike a while loop there is no counter to manage. Python stops automatically when the list runs out.
Anything you can loop over is called an iterable. Lists, tuples, strings, dictionaries, sets, open files and the results of functions such as range() are all iterables, and later in the course you will write your own with generators. The for loop does not care which kind it is given; it simply asks for the next item until there are none left.
Totals and running calculations
A very common pattern is to start an accumulator at zero and add to it inside the loop.
daily_sales = [1250.50, 980.00, 1430.25, 1105.75, 1600.00]
total = 0
for amount in daily_sales:
total += amount
print(f"Weekly total: {total:,.2f}")
print(f"Average day: {total / len(daily_sales):,.2f}")
Output: Weekly total: 6,366.50 Average day: 1,273.30
The range() function
range() generates a sequence of integers. It takes a stop value, or start and stop, or start, stop and step. The stop value itself is never included.
| Call | Produces |
|---|---|
range(4) |
0, 1, 2, 3 |
range(1, 5) |
1, 2, 3, 4 |
range(0, 10, 3) |
0, 3, 6, 9 |
range(5, 0, -1) |
5, 4, 3, 2, 1 |
for year in range(2024, 2027):
print(f"Budget cycle {year}")
for q in range(1, 5):
print(f"Q{q}", end=" ")
print()
Output: Budget cycle 2024 Budget cycle 2025 Budget cycle 2026 Q1 Q2 Q3 Q4
range() does not build the whole list of numbers in memory. It produces each value on demand, so range(10_000_000) costs no more to create than range(10). If you ever need the numbers as an actual list, wrap it in list().
enumerate() for index and value
When you need the position as well as the item, use enumerate(). Pass start=1 to number from one instead of zero.
top_products = ["Laptop Pro", "Desk Lamp", "USB Hub"]
for rank, product in enumerate(top_products, start=1):
print(f"{rank}. {product}")
Output: 1. Laptop Pro 2. Desk Lamp 3. USB Hub
Ask the assistant to rewrite an index-based loop with enumerate() and to explain why the enumerate version is considered more Pythonic.
Rewrite this Python loop using enumerate(): for i in range(len(employees)): print(i + 1, employees[i], salaries[i]). Then rewrite it again using zip() so that the two lists are paired without any index at all, and explain when I would choose each version.
zip() to loop over two lists together
zip() pairs up items from two or more sequences so you can process related columns of data side by side.
names = ["Priya", "Tom", "Lena"]
hours = [38, 42, 45]
rate = 25
for name, h in zip(names, hours):
overtime = max(0, h - 40)
pay = h * rate + overtime * rate * 0.5
print(f"{name}: {h}h, pay {pay:.2f}")
Output: Priya: 38h, pay 950.00 Tom: 42h, pay 1075.00 Lena: 45h, pay 1187.50
If the sequences have different lengths, zip() silently stops at the shortest one, which can hide a data problem. From Python 3.10 you can pass strict=True to make it raise a ValueError instead, a good habit when the lists are supposed to line up, such as names and hours from the same payroll export.
Looping through strings and dictionaries
A string yields one character at a time. A dictionary yields its keys by default, or key-value pairs with .items().
for ch in "SKU":
print(ch, end="-")
print()
stock = {"chairs": 40, "desks": 12, "monitors": 0}
for item, qty in stock.items():
status = "out of stock" if qty == 0 else f"{qty} available"
print(f"{item}: {status}")
Output: S-K-U- chairs: 40 available desks: 12 available monitors: out of stock
break, continue and else
The same control keywords from the while chapter work in for loops. break exits early, continue skips to the next item and else runs only if the loop was not broken.
payments = [200, 450, 0, 300, 5000, 125]
for p in payments:
if p == 0:
continue
if p > 4000:
print(f"Flagged for review: {p}")
break
print(f"Approved: {p}")
else:
print("All payments approved")
Output: Approved: 200 Approved: 450 Approved: 300 Flagged for review: 5000
Read the else as “no break”. In the example the loop was interrupted by the 5,000 payment, so the all-approved message did not appear. Remove that payment from the list and the else block runs.
Nested for loops
A loop inside a loop is useful for grids and combinations. The inner loop runs fully for each item of the outer loop.
branches = ["Mumbai", "Pune"]
quarters = ["Q1", "Q2", "Q3"]
for branch in branches:
for q in quarters:
print(f"{branch}-{q}", end=" ")
print()
Output: Mumbai-Q1 Mumbai-Q2 Mumbai-Q3 Pune-Q1 Pune-Q2 Pune-Q3
for x in items[:]) or build a new list instead.List comprehensions: the compact for loop
When a loop only builds a new list, a comprehension does the same job in one line. The syntax is [expression for item in sequence if condition].
prices = [120, 85, 300, 45]
with_tax = [round(p * 1.18, 2) for p in prices]
expensive = [p for p in prices if p > 100]
print(with_tax)
print(expensive)
Output: [141.6, 100.3, 354.0, 53.1] [120, 300]
Hand the assistant a nested loop and ask it to convert it into a comprehension, then ask which version is easier to read and why.
I have two Python lists: products = ["Chair", "Desk"] and colours = ["Black", "White", "Oak"]. Write a nested for loop that prints every product-colour combination, then rewrite it as a single list comprehension that produces strings like "Chair - Black". Tell me which version you would use in production code and why.
Comprehensions are ideal for a single transformation or filter. Once you need two conditions, a nested loop and a function call in the same line, readability drops fast, so switch back to a normal for loop. A useful rule: if the comprehension does not fit comfortably on one line, it should be a loop.
Common mistakes
- Assuming
range(1, 10)includes 10. The stop value is excluded. - Modifying the list you are iterating over, which skips items.
- Using
range(len(items))when you only need the values; loop over the list directly. - Reusing the loop variable name for something else inside the body, which silently overwrites it.
- Forgetting
.items()on a dictionary, so the loop yields only keys.
Exercise
Given invoices = [("INV-01", 500), ("INV-02", 1200), ("INV-03", 75), ("INV-04", 2600)], use a for loop with tuple unpacking to print each invoice number and amount, keep a running total, and after the loop print the total and how many invoices were above 1000.
Show answer
invoices = [("INV-01", 500), ("INV-02", 1200), ("INV-03", 75), ("INV-04", 2600)]
total = 0
large = 0
for number, amount in invoices:
print(f"{number}: {amount}")
total += amount
if amount > 1000:
large += 1
print(f"Total {total}, {large} invoices above 1000")
Output: INV-01: 500 INV-02: 1200 INV-03: 75 INV-04: 2600 Total 4375, 2 invoices above 1000
Related chapters
FAQ
How does range() work in a Python for loop?
range(start, stop, step) produces integers from start up to but not including stop. range(5) gives 0 to 4, and a negative step counts downwards.
How do I get the index in a Python for loop?
Wrap the sequence in enumerate(). It yields (index, value) pairs, and enumerate(items, start=1) makes the numbering begin at one.
Can a Python for loop iterate over two lists at once?
Yes. zip(list_a, list_b) pairs the items positionally so you can unpack both values in the loop header. It stops at the shorter list.
Working with spreadsheets too? Ready-made Excel, Google Sheets and Power BI templates are at NextGenTemplates.com.
Chapter 19 of 48 · Python with AI: all 48 chapters



