Section · 01
The basic shape
A forloop says “for each item in this collection, run this block.” The iteration variable takes a new value each time around:
orders = [42.50, 89.00, 17.95, 230.00]
for amount in orders:
print(f"Charging ${amount:.2f}")Same shape works on anything that’s “iterable” — strings, tuples, sets, dicts, file handles, even custom objects you define yourself later:
# string: iterates characters
for ch in "Python":
print(ch)
# dict: iterates keys
for key in {"a": 1, "b": 2}:
print(key)
# range: iterates numbers (more on this below)
for i in range(3):
print(i)Section · 02
range — generate sequences of numbers
Most of the time you want “do this N times” or “count from A to B.” That’s what range is for. Three forms:
range(5) # 0, 1, 2, 3, 4 — stop only
range(2, 6) # 2, 3, 4, 5 — start, stop
range(0, 20, 5) # 0, 5, 10, 15 — start, stop, step
range(10, 0, -1) # 10, 9, 8, ..., 1 — counting down (negative step)Three things to remember about range:
1. The stop value is EXCLUSIVE. range(5) goes 0..4, not 0..5.
2. range() doesn't build a list — it generates numbers on demand.
That's why range(1_000_000_000) is fast and doesn't eat your RAM.
3. To get a real list from a range, wrap it: list(range(5)) → [0, 1, 2, 3, 4]Section · 03
enumerate — when you need the index too
Sometimes you want both the position and the value. Beginners write this:
# DON'T do this:
for i in range(len(orders)):
print(f"{i}: ${orders[i]:.2f}")It works, but it’s un-Pythonic and slower than it looks. Reach for enumerate instead:
for i, amount in enumerate(orders):
print(f"{i}: ${amount:.2f}")
# 0: $42.50
# 1: $89.00
# 2: $17.95
# 3: $230.00Pass start=1 if you want 1-based numbering for display:
for n, amount in enumerate(orders, start=1):
print(f"Order #{n}: ${amount:.2f}")Section · 04
zip — walk two collections in parallel
When you have two related lists, pair them up with zip:
names = ["Ada", "Ben", "Cal"]
scores = [92, 71, 84]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# Ada: 92
# Ben: 71
# Cal: 84zipstops at the shortest list, so you don’t have to check lengths. It also works on three or more iterables at once. Pair it with dict() to build a dict from two parallel lists:
scoreboard = dict(zip(names, scores))
# {"Ada": 92, "Ben": 71, "Cal": 84}Section · 05
Looping over dicts
A bare for x in some_dictgives you keys. Most of the time you want both keys and values — that’s .items():
prefs = {"theme": "dark", "language": "en", "page_size": 25}
for key in prefs: # just keys
print(key)
for value in prefs.values(): # just values
print(value)
for key, value in prefs.items(): # both — the most common form
print(f"{key} = {value}")Unpacking the (key, value) tuple in the loop header is exactly the same move as unpacking from zip or enumerate— Python has one consistent pattern for “give each item a name as I loop over it.”
Section · 06
List comprehensions — for loops in one line
Two patterns show up so often they deserve a shortcut: building a new list by transforming an old one, and filtering a list. Python gives you a one-line form for both, called a list comprehension:
# Transform: list of squares
numbers = [1, 2, 3, 4, 5]
# Long form:
squares = []
for n in numbers:
squares.append(n * n)
# Comprehension:
squares = [n * n for n in numbers]
# [1, 4, 9, 16, 25]
# Filter: only even numbers
evens = [n for n in numbers if n % 2 == 0]
# [2, 4]
# Combined transform + filter
even_squares = [n * n for n in numbers if n % 2 == 0]
# [4, 16]The shape is always [expression for item in iterable if condition]. The if is optional.
Comprehensions are great when they stay simple. The moment they get nested or grow a multi-clause condition, write the boring for-loop version — it’ll read better. The skill is knowing when each is the right call, and that comes with practice.
Same idea for dicts and sets
# Dict comprehension
scoreboard = {name: score for name, score in zip(names, scores)}
# Set comprehension
unique_lengths = {len(word) for word in ["python", "for", "everybody"]}
# {3, 6, 9}