A nested loop is simply a loop placed inside another loop — the inner loop runs completely for every single iteration of the outer loop. They're the standard way to work with grid-like data, generate patterns, or compare every item in one collection against every item in another. Python loops also have a lesser-known feature: an else clause, which behaves very differently from an if...else.
Nested Loops
For every single pass of the outer loop, the inner loop runs through all of its own iterations before the outer loop moves on. A classic example is printing a small grid of coordinates:
for row in range(1, 4):
for col in range(1, 4):
print(f"({row},{col})", end=" ")
print() # move to a new line after each row
Output:
(1,1) (1,2) (1,3)
(2,1) (2,2) (2,3)
(3,1) (3,2) (3,3)
For a 3×3 grid like this, the inner loop runs 3 times for every outer iteration — 9 total iterations. This is exactly the structure behind printing number/star patterns, and it's also how you'd compare two lists element-by-element or build a multiplication table.
# simple multiplication table for 1-3
for i in range(1, 4):
for j in range(1, 4):
print(f"{i} x {j} = {i * j}", end=" ")
print()
Output:
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 = 9
The Loop else Clause
Both for and while loops in Python can have an else block. It's one of the most surprising parts of Python's syntax for newcomers, because it has nothing to do with whether the loop body ran — it runs only if the loop finished without hitting a break.
numbers = [2, 4, 6, 8]
for n in numbers:
if n % 2 != 0:
print("Found an odd number!")
break
else:
print("All numbers are even.")
Output:
All numbers are even.
If you change one number in the list to be odd, the break fires, and the else block is skipped entirely — it only runs when the loop completes naturally. A good mental model: think of it as "loop...else" meaning "if the loop never broke out early".
When This Is Actually Useful
The loop else is most useful for search-style loops — "search for something, and if you never find it, do this instead." It avoids needing a separate flag variable to track whether the loop broke early, which is the alternative most other languages force you into.
