Python’s for loop is different from JavaScript’s for loop in a fundamental way โ it is a for-each loop that iterates over any iterable object directly, rather than incrementing an index variable. This makes Python loops more readable and less error-prone: there is no off-by-one error, no need to track an index variable, and no need to call .length. Python’s for loop works seamlessly with lists, tuples, strings, dictionaries, sets, generators, and any object that implements the iterator protocol. You will use for loops constantly in FastAPI to process database query results, transform API response data, and validate collections of values.
Basic for Loop
# Iterate directly over items โ no index needed
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# apple
# banana
# cherry
# Iterate over a string (character by character)
for char in "hello":
print(char) # h, e, l, l, o
# Iterate over a range of numbers
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for i in range(1, 6): # 1, 2, 3, 4, 5
print(i)
for i in range(0, 10, 2): # 0, 2, 4, 6, 8 (step=2)
print(i)
for i in range(10, 0, -1): # 10, 9, 8, ... 1 (countdown)
print(i)
range() does not create a list in memory โ it is a lazy sequence that generates numbers on demand. This means range(1_000_000) uses almost no memory, while [0, 1, 2, ..., 999999] uses about 8MB. This matters in FastAPI when processing large datasets: iterating over a range or a database cursor is memory-efficient, while converting them to a list first wastes memory.enumerate() when you need both the index and the value โ it is more Pythonic than using range(len(iterable)) to get an index. The syntax is for i, value in enumerate(items):. You can also start the counter at a different number: enumerate(items, start=1) starts counting from 1 instead of 0. This is cleaner than manually tracking a counter variable.RuntimeError. If you need to remove items from a list, iterate over a copy: for item in items[:]: or collect items to remove in a separate list and remove them after the loop. This is a common source of subtle bugs when filtering FastAPI query results.enumerate() โ Index and Value Together
posts = ["First Post", "Second Post", "Third Post"]
# Old way โ verbose and error-prone
for i in range(len(posts)):
print(f"{i}: {posts[i]}")
# Pythonic way โ use enumerate()
for i, post in enumerate(posts):
print(f"{i}: {post}")
# 0: First Post
# 1: Second Post
# 2: Third Post
# Start index at 1 (for human-readable numbering)
for i, post in enumerate(posts, start=1):
print(f"Post {i}: {post}")
# Post 1: First Post
# Post 2: Second Post
# Post 3: Third Post
# Practical FastAPI use: add position to response items
results = db.get_posts()
numbered = [{"rank": i, "post": p} for i, p in enumerate(results, start=1)]
zip() โ Looping Over Multiple Sequences
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
grades = ["A", "B+", "A-"]
# Loop over multiple lists in parallel
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 sequence
a = [1, 2, 3, 4, 5]
b = ["a", "b", "c"]
list(zip(a, b)) # [(1, 'a'), (2, 'b'), (3, 'c')] โ stops at 3
# Create a dict from two lists
keys = ["name", "age", "role"]
values = ["Alice", 30, "admin"]
user = dict(zip(keys, values))
# {"name": "Alice", "age": 30, "role": "admin"}
Iterating Over Dictionaries
user = {"name": "Alice", "age": 30, "role": "admin"}
# Iterate over keys (default)
for key in user:
print(key) # name, age, role
# Iterate over values
for value in user.values():
print(value) # Alice, 30, admin
# Iterate over key-value pairs
for key, value in user.items():
print(f"{key}: {value}")
# name: Alice
# age: 30
# role: admin
# Practical use: filter a response dict
def sanitise_user(user_dict: dict) -> dict:
excluded = {"password", "password_hash", "reset_token"}
return {k: v for k, v in user_dict.items() if k not in excluded}
# Removes sensitive fields before sending to the client
Common Mistakes
Mistake 1 โ Modifying a list while iterating over it
โ Wrong โ removes items during iteration, skips some:
items = [1, 2, 3, 4, 5]
for item in items:
if item % 2 == 0:
items.remove(item) # Bug! Skips items as list shrinks
print(items) # [1, 3, 5] โ looks correct but skipped item 4 in some cases
โ Correct โ use a list comprehension to filter:
items = [1, 2, 3, 4, 5]
items = [item for item in items if item % 2 != 0]
print(items) # [1, 3, 5] โ
Mistake 2 โ Using range(len()) when enumerate() is cleaner
โ Wrong โ JavaScript-style index loop:
for i in range(len(posts)):
print(f"{i}: {posts[i]}") # verbose and error-prone
โ Correct โ enumerate():
for i, post in enumerate(posts):
print(f"{i}: {post}") # โ cleaner
Mistake 3 โ Forgetting range() is exclusive of the end
โ Wrong โ expecting range(1, 5) to include 5:
for i in range(1, 5):
print(i) # 1, 2, 3, 4 โ NOT 5!
โ Correct โ range end is exclusive, use range(1, 6) to include 5:
for i in range(1, 6):
print(i) # 1, 2, 3, 4, 5 โ
Quick Reference
| Pattern | Code |
|---|---|
| Iterate list | for item in items: |
| Count from 0 to n-1 | for i in range(n): |
| Count from a to b-1 | for i in range(a, b): |
| Count with step | for i in range(0, 10, 2): |
| Index + value | for i, v in enumerate(items): |
| Two lists together | for a, b in zip(list1, list2): |
| Dict keys | for key in d: |
| Dict values | for val in d.values(): |
| Dict pairs | for k, v in d.items(): |