Python Loops — for, while, and Iterator Patterns
Loops let you execute a block of code repeatedly. Python provides two primary loop
constructs — for and while — along with powerful iteration utilities. This tutorial
covers everything from basic syntax to advanced iterator techniques used in production
Python code.
Learning Objectives
- Understand
forandwhileloop syntax and when to use each - Master
range(),enumerate(),zip(), and other iteration tools - Use
break,continue, and theelseclause effectively - Recognize and avoid common loop mistakes
- Apply accumulator, filter, and transform patterns
- Write efficient loops that avoid unnecessary computation
The for Loop
The for loop iterates over any iterable — lists, tuples, strings, dicts, sets, ranges,
and more. Python's for is a foreach loop: it visits each element in order.
for variable in iterable:
# do something with variable
Iterating Over Sequences
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# apple
# banana
# cherry
coordinates = (10, 20, 30)
for coord in coordinates:
print(f"Coordinate: {coord}")
# Coordinate: 10
# Coordinate: 20
# Coordinate: 30
The range() Function
range(start, stop, step) generates a sequence of integers. The sequence stops
before stop.
# 0 to 4
for i in range(5):
print(i, end=" ") # 0 1 2 3 4
# Custom start and stop
for i in range(2, 7):
print(i, end=" ") # 2 3 4 5 6
# With step
for i in range(0, 20, 3):
print(i, end=" ") # 0 3 6 9 12 15 18
# Counting backwards
for i in range(10, 0, -2):
print(i, end=" ") # 10 8 6 4 2
Iterating Over Dictionaries
person = {"name": "Alice", "age": 30, "city": "Portland"}
for key in person: # keys (default)
print(key)
for key in person.keys(): # keys explicitly
print(key)
for value in person.values(): # values
print(value)
for key, value in person.items(): # key-value pairs
print(f"{key}: {value}")
Iterating Over Sets and Strings
unique_numbers = {1, 2, 3, 4, 5}
for num in unique_numbers:
print(num, end=" ") # order not guaranteed
for char in "Python":
print(char, end=" ") # P y t h o n
Nested for Loops
for i in range(3):
for j in range(3):
print(f"({i},{j})", end=" ")
print()
# (0,0) (0,1) (0,2)
# (1,0) (1,1) (1,2)
# (2,0) (2,1) (2,2)
The while Loop
A while loop continues as long as its condition is True. Use it when you do not
know in advance how many iterations you need.
while condition:
# code block — condition must eventually become False
Simple Counter
count = 0
while count < 5:
print(count, end=" ")
count += 1
# 0 1 2 3 4
Infinite Loops (When They Are Useful)
Infinite loops are intentional in servers, game loops, and event handlers. Always provide an exit mechanism.
running = True
frame = 0
while running:
frame += 1
if frame >= 3:
running = False
print(f"Frame {frame}")
# Frame 1
# Frame 2
# Frame 3
The while True Pattern
iteration = 0
max_iterations = 5
while True:
iteration += 1
if iteration > max_iterations:
break
print(f"Processing item {iteration}")
# Processing item 1 through 5
Sentinel Value Loop
values = [10, 25, 37, 42, 58, 63, 71]
index = 0
while index < len(values) and values[index] < 50:
print(values[index], end=" ")
index += 1
# 10 25 37 42
break and continue
break — Exit the Loop Early
break immediately terminates the innermost loop.
numbers = [3, 7, 2, 9, 4, 6, 8]
for num in numbers:
if num > 5:
print(f"First number greater than 5: {num}")
break
# First number greater than 5: 7
continue — Skip to Next Iteration
continue jumps to the next iteration, skipping the rest of the body.
for i in range(10):
if i % 2 == 0:
continue
print(i, end=" ")
# 1 3 5 7 9
Combining break and continue
for i in range(20):
if i % 3 == 0:
continue
if i > 14:
break
print(i, end=" ")
# 1 2 4 5 7 8 10 11 13 14
The for-else and while-else
Python loops can have an else clause. It runs only if the loop completes without
hitting break. Think of it as "no break occurred," not "condition is false."
Search Pattern
primes = [2, 3, 5, 7, 11, 13, 17]
target = 19
for prime in primes:
if prime == target:
print(f"Found {target}!")
break
else:
print(f"{target} is not in the list")
# 19 is not in the list
target = 7
for prime in primes:
if prime == target:
print(f"Found {target}!")
break
else:
print(f"{target} is not in the list")
# Found 7!
Common Misunderstanding
# else runs because loop finished normally (no break)
for i in range(3):
print(i)
else:
print("Loop completed without break")
# 0
# 1
# 2
# Loop completed without break
enumerate()
enumerate() adds a counter to any iterable, returning (index, value) tuples.
colors = ["red", "green", "blue"]
for index, color in enumerate(colors):
print(f"{index}: {color}")
# 0: red
# 1: green
# 2: blue
Custom Start Index
for index, color in enumerate(colors, start=1):
print(f"{index}: {color}")
# 1: red
# 2: green
# 3: blue
Why Not range(len())?
enumerate() is better because it works with any iterable, avoids off-by-one errors,
is more readable, and is slightly faster in CPython. Prefer it over range(len(...)).
zip()
zip() combines multiple iterables into tuples, stopping at the shortest one.
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# Alice: 85
# Bob: 92
# Charlie: 78
Creating a Dictionary with zip
keys = ["name", "age", "city"]
values = ["Alice", 30, "Portland"]
person = dict(zip(keys, values))
# {'name': 'Alice', 'age': 30, 'city': 'Portland'}
Strict Mode (Python 3.10+)
With strict=True, zip raises ValueError if lengths differ:
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92]
try:
list(zip(names, scores, strict=True))
except ValueError as e:
print(f"Error: {e}")
# Error: zip() has arguments with different lengths
itertools.zip_longest
Process all elements, filling missing values:
from itertools import zip_longest
names = ["Alice", "Bob", "Charlie", "Diana"]
scores = [85, 92]
for name, score in zip_longest(names, scores, fillvalue="N/A"):
print(f"{name}: {score}")
# Alice: 85
# Bob: 92
# Charlie: N/A
# Diana: N/A
Iterating Techniques
Reversed()
Iterate in reverse without modifying the original:
numbers = [1, 2, 3, 4, 5]
for num in reversed(numbers):
print(num, end=" ")
# 5 4 3 2 1
any() and all() with Generators
numbers = [2, 4, 6, 8, 10]
print(all(n % 2 == 0 for n in numbers)) # True
print(any(n > 7 for n in numbers)) # True
print(any(n < 0 for n in numbers)) # False
Loop Patterns
Accumulator Pattern
Collect results as you iterate:
numbers = [1, 2, 3, 4, 5]
total = 0
for num in numbers:
total += num
print(f"Sum: {total}") # Sum: 15
Filter Pattern
Keep only items that match:
scores = [85, 42, 91, 38, 76, 55, 88]
passing = [s for s in scores if s >= 60]
print(passing) # [85, 91, 76, 88]
Transform Pattern
Convert each element:
celsius = [0, 10, 20, 30, 40]
fahrenheit = [t * 9 / 5 + 32 for t in celsius]
print(fahrenheit) # [32.0, 50.0, 68.0, 86.0, 104.0]
Find First / Any / All
data = [3, 7, 11, 2, 9, 4]
first_even = None
for num in data:
if num % 2 == 0:
first_even = num
break
print(f"First even: {first_even}") # First even: 2
all_positive = all(num > 0 for num in data)
print(f"All positive: {all_positive}") # All positive: True
Dictionary Building in Loops
words = ["apple", "banana", "cherry", "avocado", "blueberry"]
grouped = {}
for word in words:
letter = word[0]
if letter not in grouped:
grouped[letter] = []
grouped[letter].append(word)
# {'a': ['apple', 'avocado'], 'b': ['banana', 'blueberry'], 'c': ['cherry']}
Nested Loops vs List Comprehensions
# Nested loop
pairs = []
for i in range(4):
for j in range(i + 1, 4):
pairs.append((i, j))
# Equivalent comprehension
pairs = [(i, j) for i in range(4) for j in range(i + 1, 4)]
# [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
# Flattening nested lists
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [val for row in matrix for val in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Performance
for vs while
for loops are faster because they are optimized at the C level. Use for when
possible; use while only when the exit condition depends on runtime logic.
import time
n = 1_000_000
start = time.time()
for i in range(n):
pass
for_time = time.time() - start
start = time.time()
i = 0
while i < n:
i += 1
while_time = time.time() - start
print(f"for: {for_time:.4f}s") # ~0.05s
print(f"while: {while_time:.4f}s") # ~0.08s
Generator Expressions for Memory
import sys
# List — stores everything in memory
squares_list = [x ** 2 for x in range(1_000_000)]
# Generator — yields one at a time
squares_gen = (x ** 2 for x in range(1_000_000))
print(sys.getsizeof(squares_list)) # ~8 MB
print(sys.getsizeof(squares_gen)) # ~200 bytes
Common Mistakes
Infinite while Loops
# Wrong — forgot to update condition
x = 0
# while x < 5:
# print(x) # infinite loop!
# Right
x = 0
while x < 5:
print(x, end=" ")
x += 1
# 0 1 2 3 4
Modifying a List During Iteration
# Wrong — skips elements
numbers = [1, 2, 3, 4, 5]
# for num in numbers:
# if num % 2 == 0:
# numbers.remove(num)
# Right — iterate over a copy
numbers = [1, 2, 3, 4, 5]
for num in numbers[:]:
if num % 2 == 0:
numbers.remove(num)
print(numbers) # [1, 3, 5]
range() Off-by-One
items = ["a", "b", "c", "d"]
# Use zip to iterate pairs instead of index math
for a, b in zip(items, items[1:]):
print(a, b)
# a b
# b c
# c d
else Clause Confusion
# else runs when loop completes without break
for i in range(5):
pass
else:
print("Runs — no break occurred")
for i in range(5):
if i == 3:
break
else:
print("Does NOT print — break occurred")
Not Using enumerate
fruits = ["apple", "banana", "cherry"]
# Not Pythonic
i = 0
for fruit in fruits:
print(f"{i}: {fruit}")
i += 1
# Pythonic
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
Practice Exercises
Exercise 1: Factorial Calculator
Write a loop that computes the factorial of a number (n!) using both for and while.
Solution
def factorial_for(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
def factorial_while(n):
result = 1
i = 1
while i <= n:
result *= i
i += 1
return result
print(factorial_for(5)) # 120
print(factorial_while(5)) # 120
Exercise 2: FizzBuzz
Print 1 through 30. Multiples of 3 → "Fizz". Multiples of 5 → "Buzz". Both → "FizzBuzz".
Solution
for i in range(1, 31):
if i % 3 == 0 and i % 5 == 0:
print("FizzBuzz")
elif i % 3 == 0:
print("Fizz")
elif i % 5 == 0:
print("Buzz")
else:
print(i)
Exercise 3: Find Duplicates
Find all duplicate elements in a list using enumerate and a set.
Solution
numbers = [1, 3, 5, 3, 7, 1, 9, 5, 3]
seen = set()
duplicates = set()
for num in numbers:
if num in seen:
duplicates.add(num)
seen.add(num)
print(f"Duplicates: {sorted(duplicates)}")
# Duplicates: [1, 3, 5]
Key Takeaways
| Concept | Syntax | When to Use |
|---|---|---|
for loop | for x in iter: | Known or finite iterations |
while loop | while cond: | Unknown iteration count |
range() | range(start, stop, step) | Numeric sequences |
enumerate() | enumerate(iter, start=0) | Need index + value |
zip() | zip(a, b, ...) | Parallel iteration |
break | break | Exit loop early |
continue | continue | Skip current iteration |
else | else: (after loop) | Run if no break occurred |
- Use
forwhen iterating over a known sequence;whilewhen the exit condition depends on runtime logic. enumerate()is almost always better thanrange(len(...)).zip()pairs elements from multiple iterables — usestrict=Trueto catch length mismatches.- The
elseclause on loops runs when the loop finishes withoutbreak. - Prefer generator expressions for large datasets to save memory.
- Never modify a collection while iterating over it — iterate over a copy instead.