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
fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
print(fruit)
# apple
# banana
# cherryIterating over a string
Strings are iterables of characters, so looping over one yields each character in order.
Looping over a string
for letter in 'hello':
print(letter.upper())
# H
# E
# L
# L
# OIterating 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
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.00Iterating 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()
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 8The 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
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 primeNested 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
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 = 9A 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
fruits = ['apple', 'banana', 'cherry']
for index, fruit in enumerate(fruits):
print(index, fruit)
# 0 apple
# 1 banana
# 2 cherryThis 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...elseruns theelseblock only if the loop finished without hittingbreak.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).