Free GuideBeginner
35 min read
#python#control flow#if else#loops#while#for#match#iteration

Control Flow

Comprehensive guide on Control Flow.

Control Flow

Overview#

Control flow statements determine the order in which a program's instructions execute. Python provides conditional statements (if, elif, else), loops (for, while), loop control statements (break, continue, else on loops), and, since Python 3.10, structural pattern matching with match. This notebook covers all of these in depth, including truthiness rules, common iteration helpers, nested loop control, and pitfalls that trip up beginners.

1. Conditional Statements#

Python 3.12 (WASM)
age = 20 if age < 13: category = "child" elif age < 20: category = "teenager" else: category = "adult" print(category) # adult

Comparison and logical operators#

OperatorMeaning
==, !=Equal to, not equal to
<, >, <=, >=Less/greater than (or equal)
and, or, notLogical combination
in, not inMembership test
is, is notIdentity comparison
Python 3.12 (WASM)
name = "Alice" if name in ("Alice", "Bob") and age >= 18: print("Access granted")

Chained comparisons#

Python allows chaining comparisons in a way that reads naturally and evaluates each part only once.

Python 3.12 (WASM)
x = 5 print(0 < x < 10) # True, equivalent to (0 < x) and (x < 10) print(10 < x < 20) # False

The conditional (ternary) expression#

Python 3.12 (WASM)
status = "adult" if age >= 18 else "minor" # Can be nested, but readability suffers quickly — use sparingly grade = "A" if score >= 90 else "B" if score >= 80 else "C"

Truthiness: what counts as False#

Every object in Python has an implicit boolean value used in if statements, even if it isn't a literal True/False.

Falsy valuesTruthy values
False, None, 0, 0.0Any nonzero number
"" (empty string)Any non-empty string
[], (), {}, set()Any non-empty collection
Python 3.12 (WASM)
items = [] if items: print("has items") else: print("empty") # this runs — empty list is falsy # Idiomatic emptiness check if not items: print("nothing to process")

2. for Loops#

A for loop iterates over any iterable — lists, tuples, strings, dictionaries, ranges, files, and custom objects that implement iteration.

Python 3.12 (WASM)
fruits = ["apple", "banana", "cherry"] for fruit in fruits: print(fruit) for i in range(5): # 0, 1, 2, 3, 4 print(i) for i in range(2, 10, 2): # start, stop, step -> 2, 4, 6, 8 print(i)

Useful iteration helpers#

Python 3.12 (WASM)
fruits = ["apple", "banana", "cherry"] # enumerate: get index + value together for i, fruit in enumerate(fruits): print(i, fruit) # 0 apple, 1 banana, 2 cherry for i, fruit in enumerate(fruits, start=1): # start counting from 1 print(i, fruit) # zip: iterate multiple sequences in parallel prices = [1.5, 0.8, 2.0] for fruit, price in zip(fruits, prices): print(f"{fruit}: ${price}") # reversed: iterate backwards for fruit in reversed(fruits): print(fruit) # sorted with iteration for fruit in sorted(fruits, reverse=True): print(fruit)

Looping over a dictionary#

Python 3.12 (WASM)
scores = {"Alice": 90, "Bob": 85} for name in scores: # iterates keys by default print(name) for name, score in scores.items(): # keys and values together print(f"{name}: {score}")

3. while Loops#

A while loop repeats as long as a condition remains true. Useful when the number of iterations isn't known ahead of time — waiting for user input, polling until a condition is met, and so on.

Python 3.12 (WASM)
count = 0 while count < 5: print(count) count += 1

while with a sentinel value#

Python 3.12 (WASM)
responses = [] while True: answer = input("Enter a value (or 'done'): ") if answer == "done": break responses.append(answer)

Avoiding infinite loops#

Always ensure the loop's condition will eventually become false — update the relevant variable inside the loop body.

Python 3.12 (WASM)
# Dangerous: condition never changes # while True: # print("forever") # Safe pattern with a clear exit condition attempts = 0 max_attempts = 3 while attempts < max_attempts: success = try_connection() if success: break attempts += 1 else: print("All attempts failed")

4. break, continue, and Loop else#

Python 3.12 (WASM)
# break: exit the loop early, entirely for n in range(10): if n == 5: break print(n) # prints 0-4 # continue: skip the rest of this iteration, move to the next for n in range(5): if n % 2 == 0: continue print(n) # prints 1, 3 # else on a loop: runs only if the loop completes WITHOUT break for n in range(5): if n == 10: break else: print("Loop completed without break") # this runs

The else clause on loops is a Python-specific feature that often surprises newcomers coming from other languages — it does not mean "if the loop didn't run," but "if the loop finished normally, without a break." A common real use case is searching for something and reporting failure only if nothing was found:

Python 3.12 (WASM)
target = 7 numbers = [1, 3, 5, 9] for n in numbers: if n == target: print("Found it") break else: print("Not found") # runs because the loop never broke

5. Nested Loops and Loop Control#

Python 3.12 (WASM)
for i in range(3): for j in range(3): print(f"({i}, {j})")

break and continue only affect the innermost loop they're written in — they cannot directly exit an outer loop.

Breaking out of nested loops#

Python has no built-in "labeled break" like some other languages. Common patterns to escape multiple levels:

Python 3.12 (WASM)
# Pattern 1: use a flag found = False for i in range(5): for j in range(5): if i * j == 6: found = True break if found: break # Pattern 2: extract the nested loops into a function and return def find_pair(target): for i in range(5): for j in range(5): if i * j == target: return (i, j) return None result = find_pair(6)

The function-based approach is usually the cleanest and most readable.

6. Structural Pattern Matching (match)#

Introduced in Python 3.10, match provides a more expressive alternative to long if/elif chains, especially for matching on structure, type, or specific values.

Python 3.12 (WASM)
def describe(value): match value: case 0: return "zero" case int() if value < 0: return "negative integer" case int(): return "positive integer" case str(): return "a string" case [first, *rest]: return f"a list starting with {first}" case {"name": name}: return f"a dict with name {name}" case _: return "something else" print(describe(0)) # zero print(describe(-5)) # negative integer print(describe("hi")) # a string print(describe([1, 2, 3])) # a list starting with 1 print(describe({"name": "Al"})) # a dict with name Al

Matching multiple values with |#

Python 3.12 (WASM)
def is_weekend(day): match day: case "Saturday" | "Sunday": return True case _: return False

Matching class instances#

Python 3.12 (WASM)
class Point: def __init__(self, x, y): self.x = x self.y = y def locate(point): match point: case Point(x=0, y=0): return "origin" case Point(x=0, y=y): return f"on the y-axis at {y}" case Point(x=x, y=0): return f"on the x-axis at {x}" case Point(): return "somewhere else" case _: return "not a point"

The case _: branch acts as a catch-all, similar to else, and should generally be included to handle unexpected values gracefully.

7. Common Pitfalls#

Modifying a list while iterating over it#

Python 3.12 (WASM)
# Dangerous: mutating a list while looping over it skips elements numbers = [1, 2, 3, 4, 5] for n in numbers: if n % 2 == 0: numbers.remove(n) # unpredictable results! # Safer: iterate over a copy, or build a new list numbers = [1, 2, 3, 4, 5] numbers = [n for n in numbers if n % 2 != 0]

Off-by-one errors with range#

Python 3.12 (WASM)
# range(n) goes from 0 to n-1, NOT including n for i in range(5): print(i) # 0, 1, 2, 3, 4 — five values, not "up to 5"

Confusing elif chains with independent if statements#

Python 3.12 (WASM)
score = 85 # Using separate if statements: ALL conditions are checked if score >= 60: print("Pass") if score >= 80: print("Distinction") # both print — these are independent checks # Using elif: only ONE branch runs if score >= 80: print("Distinction") elif score >= 60: print("Pass") # only "Distinction" prints

Notes#

  • Prefer for loops when iterating over a known collection or range.
  • Prefer while loops when the number of iterations depends on a runtime condition rather than a fixed sequence.
  • enumerate() and zip() are idiomatic Python — reaching for manual index tracking is usually a sign a built-in helper would be cleaner.
  • Overusing break/continue in deeply nested loops can make control flow hard to follow; extracting logic into a function often helps.
  • match is not just a switch statement — it supports deep structural matching on lists, dicts, and class instances, which if/elif cannot express as cleanly.
  • Python has no traditional do-while loop; it is typically simulated with while True plus an internal break condition.
  • Never mutate a list (or other collection) while iterating over it directly — iterate over a copy or build a new collection instead.

Best Practices Checklist#

  • Use elif instead of separate if statements for mutually exclusive conditions
  • Prefer for over while when iterating a known sequence
  • Always ensure while loop conditions can become false
  • Use enumerate() instead of manually tracking an index in a for loop
  • Use zip() to iterate multiple sequences together instead of indexing into each separately
  • Reach for match when checking a value's type or structure across many cases
  • Keep nested loops shallow — extract inner logic into a function if nesting grows deep
  • Use the loop else clause only when its "ran without break" meaning is clear to readers
  • Never modify a list while iterating over it directly
  • Rely on truthiness checks (if not items:) rather than explicit comparisons like if len(items) == 0:
Knowledge Checkpoint

Control Flow & Pattern Matching Checkpoint

Q1.In a Python `for...else` or `while...else` loop, when does the `else` block execute?
AOnly when the loop encounters a `break` statement.
BOnly when the loop completes all iterations normally without encountering a `break`.
CWhenever an exception is thrown inside the loop.
DEvery single iteration of the loop.
Q2.Which statement immediately halts the current loop iteration and skips directly to the next iteration?
Abreak
Bcontinue
Cpass
Dreturn
Q3.Which syntax was introduced in Python 3.10 for structural pattern matching?
Aswitch / case
Bmatch / case
Cselect / when
Dcond / branch
Track Your Learning

Finished studying this notebook?

Mark this guide as completed to update your course progress roadmap.