Last 30 Days
No notifications
Control flow determines the order in which statements execute. Python uses if, elif, and else to make decisions based on conditions.
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"
print(grade) # B| Concept | Example | Result |
| Truthy | if "hello": | Executes (non-empty string) |
| Falsy | if 0: | Skipped |
| Falsy values | 0, 0.0, "", None, [], {}, () | All evaluate to False |
| Everything else | Non-zero, non-empty values | Evaluates to True |
age = 25
has_ticket = True
if age >= 18:
if has_ticket:
print("Welcome!")
else:
print("Buy a ticket first.")
else:
print("Too young.")status = "adult" if age >= 18 else "minor"The match statement is Python's version of switch-case:
command = "start"
match command:
case "start":
print("Starting...")
case "stop":
print("Stopping...")
case _:
print("Unknown command")if 18 <= age <= 65:
print("Working age")> Tip: Avoid deeply nested if statements. Use early returns or elif chains to keep code flat and readable.
Control flow is how a program *makes decisions*. So far you've written code that runs straight through; with if, elif, else, ternary expressions, and match-case, you can branch — different inputs cause different paths.
Programming = data + decisions + repetition. Control flow covers the decisions part. Python's branching primitives:
if / elif / else — branch on conditions.a if cond else b — inline branch as an expression.match / case (Python 3.10+) — structural pattern matching.for, while) are the *repetition* side and are covered separately.if age >= 18:
print("adult")
elif age >= 13:
print("teen")
else:
print("kid")Rules:
: ends every branch line.elif is short for "else if" — you can have any number; else is optional, at most one.Python evaluates conditions on truthiness, not just True/False. Falsy values: False, None, 0, 0.0, "", [], {}, set(). Everything else is truthy.
if name: # idiomatic empty check
greet(name)
if items: # "is the list non-empty?"
process(items)status = "adult" if age >= 18 else "minor"Reads left-to-right: *value if condition else other_value*. Use for simple assignments — nesting them quickly becomes unreadable.
1. Forgetting the colon. if x > 0 (no :) is a SyntaxError.
2. Mixing tabs and spaces. Pick spaces, configure once.
3. if x = 5: instead of if x == 5:. = is assignment, == is comparison.
4. Comparing to booleans explicitly. if is_ready == True: → just if is_ready:.
5. Long elif chains for ranges. Use chained comparison: if 0 <= score < 60: ....
6. Deeply nested ifs. Flatten with early returns (guard clauses) instead.
Replace nested branching with flat exits:
# Nested (hard to read)
def discount(user, total):
if user is not None:
if user.is_active:
if total > 100:
return total * 0.9
return total# Flat with guards (easier)
def discount(user, total):
if user is None: return total
if not user.is_active: return total
if total <= 100: return total
return total * 0.9
Guards keep the *happy path* at the bottom and unindented — a senior code-review favourite.
and / or stop evaluating once the answer is determined:
if user and user.is_active: # second part skipped if user is None
...
name = nickname or "guest" # default fallbackThis is also why if x is not None and len(x) > 0 is safe — len only runs when x exists.
if 0 <= score <= 100:
grade(score)
if start < end <= deadline:
schedule(...)Reads like maths and skips the manual and.
Put the common case first so most of your inputs hit the first branch (faster + clearer). Put error/edge cases last (or as guard clauses up top).
match / case (3.10+)Structural pattern matching is *not* just a switch statement — it can destructure tuples, lists, dicts, and class instances:
match point:
case (0, 0): print("origin")
case (x, 0): print(f"x-axis @ {x}")
case (0, y): print(f"y-axis @ {y}")
case (x, y) if x == y: print("on y=x")
case (x, y): print(f"({x}, {y})")
case _: print("unknown")Patterns:
case 200:case [first, *rest]:case {"type": "user", "id": id}:case Point(x=0, y=y):case 401 | 403:case x if x > 0:case _: (catch-all).match vs if/elifif/elif.match.if elif elif ... else on the same variable → match is cleaner.match everywhere; it's a tool, not a style.Long type-based if chains (if isinstance(x, A): ... elif isinstance(x, B):) are a smell. Replace with:
HANDLERS = {"GET": handle_get, ...}).functools.singledispatch for true type-based dispatch.[x if x > 0 else 0 for x in nums] # transform-conditional inside the value
[x for x in nums if x > 0] # filter-conditional after
[x if x else "-" for x in items if x is not None] # bothDifferent positions, different jobs. Mixing them is fine but parentheses help.
1. Write a grade(score) function returning A/B/C/D/F using if/elif/else and chained comparisons.
2. Refactor a 3-deep nested if into guard clauses with early returns.
3. Use match/case to handle a parsed event dict with shapes {type: 'click', ...} and {type: 'keypress', ...}.
4. Replace a long if isinstance chain with a dispatch dict mapping type strings to handler functions.