for Loops
Python's for loop iterates directly over any iterable - no index management needed.
# Iterate over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# apple
# banana
# cherry
# Iterate over a string (character by character)
for char in "Python":
print(char, end=" ") # P y t h o n
# Iterate over a dict (keys by default)
person = {"name": "Alice", "age": 30}
for key in person:
print(key, person[key])
# Iterate over dict key-value pairs
for key, value in person.items():
print(f"{key}: {value}")
# Iterate over a tuple
for x, y in [(1, 2), (3, 4), (5, 6)]:
print(f"({x}, {y})")
# Iterate over a set (unordered)
for item in {3, 1, 4, 1, 5}:
print(item) # arbitrary order
# Loop over multiple items with unpacking
records = [("Alice", 95), ("Bob", 87), ("Charlie", 92)]
for name, score in records:
print(f"{name}: {score}")
# Use _ for ignored loop variable
for _ in range(3):
print("hello") # prints 3 times, don't need the index
range()
range() generates a sequence of numbers lazily - it does not store them all in memory.
# range(stop) - 0 to stop-1
for i in range(5):
print(i, end=" ") # 0 1 2 3 4
# range(start, stop) - start to stop-1
for i in range(2, 8):
print(i, end=" ") # 2 3 4 5 6 7
# range(start, stop, step)
for i in range(0, 20, 5):
print(i, end=" ") # 0 5 10 15
# Countdown with negative step
for i in range(10, 0, -1):
print(i, end=" ") # 10 9 8 7 6 5 4 3 2 1
# range() is NOT a list - it's a range object
r = range(5)
print(type(r)) #
print(r[2]) # 2 (supports indexing)
print(3 in r) # True
print(list(r)) # [0, 1, 2, 3, 4] (convert if needed)
# Loop with index over a list - use enumerate() instead of range(len())
fruits = ["apple", "banana", "cherry"]
# WRONG (un-Pythonic):
for i in range(len(fruits)):
print(i, fruits[i])
# RIGHT (Pythonic):
for i, fruit in enumerate(fruits):
print(i, fruit)
# Reverse iteration
for i in range(len(fruits) - 1, -1, -1):
print(fruits[i])
# Better: reversed()
for fruit in reversed(fruits):
print(fruit)
while Loops
while loops run as long as the condition is truthy. Use them when the number of iterations is not known in advance.
# Basic while loop
count = 0
while count < 5:
print(count, end=" ") # 0 1 2 3 4
count += 1
# Sentinel value pattern
total = 0
while True:
entry = input("Enter number (0 to stop): ")
num = int(entry)
if num == 0:
break
total += num
print(f"Total: {total}")
# Walrus operator in while (Python 3.8+)
while line := input("Type something (blank to quit): "):
print(f"You typed: {line}")
# Retry pattern
import random
import time
attempts = 0
max_attempts = 5
while attempts < max_attempts:
if random.random() > 0.7: # 30% chance of success
print("Success!")
break
attempts += 1
print(f"Attempt {attempts} failed, retrying...")
# time.sleep(1) # in real code
else:
print("All attempts exhausted")
# Process a queue
queue = [1, 2, 3, 4, 5]
while queue:
item = queue.pop(0) # process first item
print(f"Processing {item}")
# queue might be repopulated in real code
An infinite loop with no exit condition will hang your program. Always check that: (1) the loop variable advances toward the exit condition, or (2) a break is reachable. Add a maximum iteration count as a safety net for production retry loops.
enumerate() and zip()
# --- enumerate() ---
# Adds index to any iterable
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
# 0: apple
# 1: banana
# 2: cherry
# Start index at 1
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")
# 1. apple
# 2. banana
# 3. cherry
# --- zip() ---
# Iterates over multiple iterables simultaneously
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
grades = ["A", "B", "A"]
for name, score, grade in zip(names, scores, grades):
print(f"{name}: {score} ({grade})")
# Alice: 95 (A)
# Bob: 87 (B)
# Charlie: 92 (A)
# zip() stops at the shortest iterable
short = [1, 2]
long = [10, 20, 30, 40]
print(list(zip(short, long))) # [(1, 10), (2, 20)]
# zip_longest continues to the end, filling with None
from itertools import zip_longest
print(list(zip_longest(short, long, fillvalue=0)))
# [(1, 10), (2, 20), (0, 30), (0, 40)]
# Unzip - transpose a list of tuples
pairs = [(1, "a"), (2, "b"), (3, "c")]
nums, chars = zip(*pairs) # *pairs unpacks the list
print(nums) # (1, 2, 3)
print(chars) # ('a', 'b', 'c')
# Build a dict from two lists
keys = ["name", "age", "city"]
values = ["Alice", 30, "London"]
person = dict(zip(keys, values))
print(person) # {'name': 'Alice', 'age': 30, 'city': 'London'}
# Combine enumerate and zip
for i, (name, score) in enumerate(zip(names, scores), 1):
print(f"{i}. {name} - {score}")
break, continue, and else
# break - exit the loop immediately
for n in range(10):
if n == 5:
break
print(n, end=" ") # 0 1 2 3 4
# continue - skip rest of current iteration, go to next
for n in range(10):
if n % 2 == 0:
continue # skip even numbers
print(n, end=" ") # 1 3 5 7 9
# else on a for loop - runs if loop completed without break
numbers = [2, 4, 6, 8, 10]
for n in numbers:
if n % 2 != 0:
print(f"Found odd: {n}")
break
else:
print("All numbers are even") # runs because no break occurred
# else for "search and not found"
target = 7
haystack = [1, 3, 5, 9, 11]
for item in haystack:
if item == target:
print(f"Found {target}")
break
else:
print(f"{target} not found in list") # runs - 7 is not there
# break in while loop with else
attempts = 0
while attempts < 5:
success = False # simulate
if success:
print("Connected")
break
attempts += 1
else:
print("Could not connect after 5 attempts")
# Nested loops - break only exits innermost loop
for i in range(3):
for j in range(3):
if j == 1:
break # only breaks inner loop
print(f"({i},{j})", end=" ")
print()
# (0,0) (1,0) (2,0)
# To break outer loop from inner: use a flag or function
def find_pair(matrix, target):
for i, row in enumerate(matrix):
for j, val in enumerate(row):
if val == target:
return i, j # return exits the function
return None
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(find_pair(matrix, 5)) # (1, 1)
Common Loop Patterns
import itertools
# 1. Accumulate a result
numbers = [1, 2, 3, 4, 5]
total = sum(numbers) # prefer built-in
product = 1
for n in numbers:
product *= n
print(total, product) # 15 120
# 2. Flatten a nested list
nested = [[1, 2], [3, 4], [5, 6]]
flat = [item for sublist in nested for item in sublist]
# or: list(itertools.chain.from_iterable(nested))
# 3. Group consecutive items (chunks)
data = list(range(10))
n = 3
chunks = [data[i:i+n] for i in range(0, len(data), n)]
print(chunks) # [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
# 4. Sliding window
window_size = 3
windows = [data[i:i+window_size] for i in range(len(data) - window_size + 1)]
print(windows[:3]) # [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
# 5. Count occurrences without Counter
words = "the quick brown fox jumps over the lazy dog".split()
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(sorted(counts.items(), key=lambda x: x[1], reverse=True)[:3])
# [('the', 2), ('quick', 1), ...]
# 6. Find first matching item
def first_match(iterable, predicate):
for item in iterable:
if predicate(item):
return item
return None
result = first_match([1, 3, 5, 8, 11], lambda x: x % 2 == 0)
print(result) # 8
# Or with next() + generator expression
result2 = next((x for x in [1, 3, 5, 8, 11] if x % 2 == 0), None)
print(result2) # 8
# 7. Parallel iteration with index
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
for i, (name, score) in enumerate(zip(names, scores), 1):
rank = "Gold" if i == 1 else "Silver" if i == 2 else "Bronze"
print(f"{rank}: {name} ({score})")