Notifications

No notifications

/Phase 1

Control Flow

Control Flow in Python

Control flow determines the order in which statements execute. Python uses if, elif, and else to make decisions based on conditions.

if / elif / else

score = 85
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"
print(grade)  # B

Condition Rules

ConceptExampleResult
Truthyif "hello":Executes (non-empty string)
Falsyif 0:Skipped
Falsy values0, 0.0, "", None, [], {}, ()All evaluate to False
Everything elseNon-zero, non-empty valuesEvaluates to True

Nested Conditions

age = 25
has_ticket = True
if age >= 18:
    if has_ticket:
        print("Welcome!")
    else:
        print("Buy a ticket first.")
else:
    print("Too young.")

Ternary Expression (Conditional Expression)

status = "adult" if age >= 18 else "minor"

match-case (Python 3.10+)

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")

Comparison Chaining

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.

On this page

Detailed Theory

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.

What Control Flow Actually Is

Programming = data + decisions + repetition. Control flow covers the decisions part. Python's branching primitives:

  • if / elif / else — branch on conditions.
  • Ternary a if cond else b — inline branch as an expression.
  • match / case (Python 3.10+) — structural pattern matching.
Loops (for, while) are the *repetition* side and are covered separately.

The Basic if/elif/else

if age >= 18:
    print("adult")
elif age >= 13:
    print("teen")
else:
    print("kid")

Rules:

  • The colon : ends every branch line.
  • The body is indented (4 spaces).
  • elif is short for "else if" — you can have any number; else is optional, at most one.
  • The first branch whose condition is truthy runs; the rest are skipped.

Truthy & Falsy (Quick Refresher)

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)

The Ternary Expression

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.

Beginner Mistakes to Skip

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.

Intermediate: Guard Clauses (Early Returns)

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.

Intermediate: Short-Circuit Conditions

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 fallback

This is also why if x is not None and len(x) > 0 is safe — len only runs when x exists.

Intermediate: Chained Comparisons in Conditions

if 0 <= score <= 100:
    grade(score)
if start < end <= deadline:
    schedule(...)

Reads like maths and skips the manual and.

Intermediate: Style — Order Branches by Likelihood

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).

Advanced: 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:

  • Literal: case 200:
  • Sequence: case [first, *rest]:
  • Mapping: case {"type": "user", "id": id}:
  • Class: case Point(x=0, y=y):
  • OR: case 401 | 403:
  • Guard: case x if x > 0:
  • Wildcard: case _: (catch-all).

Advanced: When to Reach for match vs if/elif

  • Few branches, simple conditions → if/elif.
  • Branching on the *shape* of data (parsing JSON, AST nodes, message types) → match.
  • Long flat if elif elif ... else on the same variable → match is cleaner.
Don't force match everywhere; it's a tool, not a style.

Advanced: Avoiding Conditionals (Polymorphism, Dispatch)

Long type-based if chains (if isinstance(x, A): ... elif isinstance(x, B):) are a smell. Replace with:

  • Polymorphism: each class implements its own method, you just call it.
  • Dispatch table: a dict mapping a key to a function (HANDLERS = {"GET": handle_get, ...}).
  • functools.singledispatch for true type-based dispatch.
Fewer branches → fewer bugs.

Advanced: Conditional Expressions in Comprehensions

[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]  # both

Different positions, different jobs. Mixing them is fine but parentheses help.

Practice Path

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.