Skip to content
C Codeloom
Python

For Loops and the range Function in Python

A practical guide to Python for loops — iterating over sequences, the range function, enumerate and zip, nested loops, and the idiomatic patterns you will use every day.

·8 min read · By Yash Kesharwani
Intermediate 10 min read

What you'll learn

  • How for loops iterate over any iterable
  • Every form of the range function
  • When to use enumerate, zip, and reversed
  • How nested loops work and when to flatten them
  • The else clause on a for loop — yes, it exists

Prerequisites

  • Comfortable with lists — see Lists
  • Comfortable with conditionals — see Conditionals

The Python for loop is one of the cleanest iteration constructs in any language. It does not count an index for you. It does not require you to manage a length. It simply walks through an iterable — a list, a string, a file, a generator, a range — and gives you each value in turn. Learn the small set of helper functions that go with it and you can express almost any iteration clearly.

The basic for loop

A for loop takes the form for <name> in <iterable>:. On each iteration, <name> is bound to the next value:

fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)

That is the whole syntax. Whatever appears after in only needs to be iterable — meaning Python can request values from it one at a time. Lists, tuples, strings, sets, dictionaries, files, and generators are all iterable.

for letter in "hello":
    print(letter)
# h e l l o (each on its own line)

Iterating a dictionary gives you keys by default; use .items() for key-value pairs:

prices = {"apple": 1.20, "banana": 0.50}
for name, price in prices.items():
    print(f"{name}: ${price:.2f}")

The range function

range produces a sequence of integers without building a list in memory. It has three forms:

range(stop)              # 0, 1, ..., stop - 1
range(start, stop)       # start, start + 1, ..., stop - 1
range(start, stop, step) # start, start + step, ... (excludes stop)

Examples:

for i in range(5):
    print(i)
# 0 1 2 3 4

for i in range(2, 6):
    print(i)
# 2 3 4 5

for i in range(0, 10, 2):
    print(i)
# 0 2 4 6 8

for i in range(10, 0, -1):
    print(i)
# 10 9 8 7 6 5 4 3 2 1

Note the exclusive upper bound — range(5) stops at 4. This matches Python’s slicing semantics and trips beginners up exactly once.

You rarely need list(range(...)). The loop works on the range object directly, and that is more memory-efficient.

Iterate over values, not indices

A common mistake from other languages is to write:

fruits = ["apple", "banana", "cherry"]
for i in range(len(fruits)):
    print(fruits[i])

This works, but it is not idiomatic. Iterate directly:

for fruit in fruits:
    print(fruit)

If you genuinely need both the index and the value, use enumerate:

for i, fruit in enumerate(fruits):
    print(i, fruit)
# 0 apple
# 1 banana
# 2 cherry

enumerate takes a start argument when you want one-based indices for human-facing output:

for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")
# 1. apple
# 2. banana
# 3. cherry

Iterate two sequences in parallel with zip

When you have two (or more) lists of related data, zip pairs them up:

names = ["Alice", "Bob", "Carol"]
ages = [30, 25, 35]

for name, age in zip(names, ages):
    print(f"{name} is {age}")

zip stops at the shortest sequence. If you need to know that lengths matched, pass strict=True (Python 3.10+) — it raises ValueError on mismatch:

for n, a in zip(names, ages, strict=True):
    ...

zip is the right answer almost every time you would otherwise reach for range(len(...)).

reversed and sorted

To iterate backwards, wrap the iterable with reversed:

for n in reversed(range(5)):
    print(n)
# 4 3 2 1 0

To iterate in sorted order without mutating the original, use sorted:

scores = [82, 47, 91, 56, 73]
for score in sorted(scores):
    print(score)

Both return new iterables — the originals are untouched. See Python Lists for how sorted differs from the in-place .sort() method.

Try it yourself. Use range and enumerate to print a multiplication table for 7 from 1 to 10, formatted like 1 x 7 = 7. Then print the same table in reverse using reversed.

Loop body essentials: break, continue, pass

Three keywords control flow inside a loop:

  • break exits the loop immediately
  • continue skips to the next iteration
  • pass does nothing (a placeholder when syntax requires a statement)
# Find the first negative number
numbers = [3, 7, -2, 9, -5]
for n in numbers:
    if n < 0:
        print("First negative:", n)
        break
# First negative: -2
# Skip empty lines
lines = ["hello", "", "world", "", "!"]
for line in lines:
    if not line:
        continue
    print(line)
# hello
# world
# !

We will see break and continue again in the while loops post.

The for ... else clause

Python for loops have an optional else clause that runs when the loop completes without hitting a break:

def find_first_negative(numbers):
    for n in numbers:
        if n < 0:
            print("Found:", n)
            break
    else:
        print("No negatives.")

find_first_negative([1, 2, 3])      # No negatives.
find_first_negative([1, -2, 3])     # Found: -2

It is genuinely useful for searches, but rare enough that many readers do not know it exists. Use it when it improves clarity and add a brief comment.

Nested loops

A loop inside a loop iterates the inner sequence once per outer step:

for row in range(1, 4):
    for col in range(1, 4):
        print(f"({row},{col})", end=" ")
    print()
# (1,1) (1,2) (1,3)
# (2,1) (2,2) (2,3)
# (3,1) (3,2) (3,3)

Nested loops are unavoidable for 2D data, but flatten where you can. Two flattening techniques:

itertools.product turns nested loops into a single one when you only care about the combination:

from itertools import product

for row, col in product(range(1, 4), range(1, 4)):
    print(f"({row},{col})", end=" ")

Comprehensions combine iteration and construction:

pairs = [(r, c) for r in range(1, 4) for c in range(1, 4)]

Modifying lists while iterating

Do not mutate a list as you iterate it — Python’s iteration position is based on indices, so deletions shift everything underneath:

nums = [1, 2, 3, 4, 5]
for n in nums:
    if n % 2 == 0:
        nums.remove(n)    # buggy — may skip elements

Build a new list instead:

nums = [1, 2, 3, 4, 5]
nums = [n for n in nums if n % 2 != 0]
print(nums)    # [1, 3, 5]

This is one of those issues that bites everyone exactly once. Once you have seen it, you will reach for a comprehension or filter automatically.

Try it yourself. Given grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], write a nested loop that computes the sum of all cells. Then rewrite it as a single expression using sum and a generator expression.

A worked example

Below is a small program that reads a list of order records and prints a summary by customer. It uses for, enumerate, dictionary iteration, and an early continue.

orders = [
    {"customer": "Alice", "amount": 49.99, "status": "paid"},
    {"customer": "Bob",   "amount": 15.00, "status": "pending"},
    {"customer": "Alice", "amount": 12.50, "status": "paid"},
    {"customer": "Carol", "amount": 30.00, "status": "refunded"},
    {"customer": "Bob",   "amount": 22.00, "status": "paid"},
]

totals = {}
for order in orders:
    if order["status"] != "paid":
        continue
    name = order["customer"]
    totals[name] = totals.get(name, 0.0) + order["amount"]

print("Paid totals by customer:")
for i, (name, total) in enumerate(sorted(totals.items()), start=1):
    print(f"  {i}. {name}: ${total:.2f}")

Output:

Paid totals by customer:
  1. Alice: $62.49
  2. Bob: $22.00

Every element of the loop — iteration, filtering, accumulation, and ordered output — is something you will reuse in real code.

Recap

You now know:

  • for iterates over any iterable, no index management required
  • range(start, stop, step) produces integer sequences cheaply
  • Prefer enumerate over range(len(...)) when you need indices
  • zip walks multiple sequences in parallel; use strict=True to catch length mismatches
  • break, continue, and the rare for ... else round out the syntax
  • Flatten nested loops with itertools.product or comprehensions when natural
  • Never mutate a list while iterating it — build a new one

Next steps

for covers most iteration, but sometimes you do not know how many iterations you need in advance. The next post covers while loops, the break/continue pair in more depth, and how to write loops that terminate safely.

→ Next: While Loops, break, and continue in Python

Questions or feedback? Email codeloomdevv@gmail.com.