Pythonfor Loops

for Loops

Python's for loop is built around iteration over any iterable — lists, strings, dictionaries, ranges, files, generators, and more — rather than the counter-based for (i = 0; i < n; i++) style found in C-like languages. If you can iterate over it, for can loop over it directly, which usually means less bookkeeping and fewer off-by-one bugs.

Iterating over a list

Looping over a list

Python
fruits = ['apple', 'banana', 'cherry']

for fruit in fruits:
    print(fruit)
# apple
# banana
# cherry
Iterating over a string

Strings are iterables of characters, so looping over one yields each character in order.

Looping over a string

Python
for letter in 'hello':
    print(letter.upper())
# H
# E
# L
# L
# O
Iterating over a dictionary

Looping directly over a dict gives you its keys. To get values or key-value pairs, use .values() or .items().

Looping over a dict

Python
prices = {'apple': 1.50, 'banana': 0.75, 'cherry': 4.00}

for key in prices:              # same as prices.keys()
    print(key)

for price in prices.values():
    print(price)

for name, price in prices.items():
    print(f'{name}: ${price:.2f}')
# apple: $1.50
# banana: $0.75
# cherry: $4.00
Iterating with range()

When you need a plain counter, or to repeat something a fixed number of times, range() generates a sequence of numbers lazily (without building a full list in memory).

Looping with range()

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

for i in range(2, 10, 2):  # start, stop, step
    print(i)  # 2 4 6 8
The for...else clause

Python's for loop supports an else clause that runs automatically when the loop finishes all its iterations without being interrupted by break. If the loop exits early via break, the else block is skipped. This is genuinely useful for "search" patterns: loop looking for something, break the moment you find it, and let else handle the "never found it" case — no extra flag variable required.

for...else: searching for a prime number

Python
def is_prime(n):
    if n < 2:
        return False
    for divisor in range(2, int(n ** 0.5) + 1):
        if n % divisor == 0:
            print(f'{n} is divisible by {divisor}, not prime')
            break
    else:
        print(f'{n} has no divisors up to its square root — it is prime')
        return True
    return False

is_prime(17)  # 17 has no divisors up to its square root — it is prime
is_prime(18)  # 18 is divisible by 2, not prime
A distinctly Pythonic feature
Developers coming from C, Java, or JavaScript are often surprised `for...else` exists at all — it has no direct equivalent in those languages. The mental model that helps it click: think of `else` as meaning "if we never broke out." It pairs especially well with search loops, avoiding a separate `found = False` flag just to check after the loop.
Nested loops

Loops can be nested inside one another — the inner loop runs to completion for every single iteration of the outer loop. This is the standard way to work with grids, tables, and combinations of two sequences.

Nested loops: a small multiplication table

Python
for i in range(1, 4):
    for j in range(1, 4):
        print(f'{i} x {j} = {i * j}', end='   ')
    print()  # newline after each row

# 1 x 1 = 1   1 x 2 = 2   1 x 3 = 3
# 2 x 1 = 2   2 x 2 = 4   2 x 3 = 6
# 3 x 1 = 3   3 x 2 = 6   3 x 3 = 9
A quick teaser: enumerate()

Often you need both the index and the value while looping over a sequence. Rather than manually tracking a counter, enumerate() hands you (index, value) pairs directly.

enumerate() preview

Python
fruits = ['apple', 'banana', 'cherry']

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

This only scratches the surface — enumerate() (and its frequent companion zip(), for looping over multiple sequences in parallel) gets a full deep-dive on its own page.

  • for item in iterable: works on lists, strings, dicts, ranges, and any other iterable.

  • Loop over a dict with .items(), .keys(), or .values() depending on what you need.

  • range(start, stop, step) generates numbers lazily without building a full list.

  • for...else runs the else block only if the loop finished without hitting break.

  • Nested loops run the full inner loop once per outer iteration — watch for O(n^2) cost on large inputs.

  • enumerate() gives you index + value pairs without manual counters (full page: enumerate-zip).