# Python Control Flow (if, loops, match)

### 1. Strategic Overview

**Control flow** defines how a program decides what to execute and when. In Python, the primary constructs are:

* `if` / `elif` / `else`
* `for` loops
* `while` loops
* `break`, `continue`, `else` (on loops)
* `match` / `case` (structural pattern matching)

These are not just syntactic elements; they encode:

* Business rules
* Decision trees
* State transitions
* Error and fallback behavior

> In production systems, control flow is where business intent becomes executable logic.

***

### 2. Enterprise Significance

Poorly designed control flow causes:

* Spaghetti logic and nested complexity
* Hidden edge cases
* Incorrect branching decisions
* Hard-to-test, fragile code
* Logic duplication across modules

Well-structured control flow provides:

* Readable and reviewable decision logic
* Predictable behavior under edge cases
* Easier refactoring and extension
* Lower cognitive load for teams
* Cleaner separation of concerns

***

### 3. Core Building Blocks of Control Flow

Key constructs:

1. **Conditionals**
   * `if`, `elif`, `else`
   * Ternary conditional expressions
2. **Loops**
   * `for` over iterables
   * `while` over Boolean conditions
3. **Loop Modifiers**
   * `break` to exit a loop
   * `continue` to skip to next iteration
   * `else` with loops for “no-break” semantics
4. **Pattern Matching**
   * `match` / `case` (Python 3.10+) for multi-branch decision logic

The art of control flow is choosing the simplest construct that correctly models the decision.

***

### 4. `if` / `elif` / `else` — Conditional Control

#### 4.1 Basic conditional

```python
if user_is_active:
    send_welcome_email()
```

#### 4.2 `if` / `elif` / `else` chain

```python
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "D"
```

Best practices:

* Keep individual conditions simple and readable.
* Order conditions from most specific to most general.
* Avoid deeply nested `if` blocks; refactor to functions or pattern matching where appropriate.

***

### 5. Truthiness and Condition Semantics

Python evaluates many values as `True` or `False` in conditionals.

Falsy values:

* `0`, `0.0`
* `None`
* Empty sequences and collections: `""`, `[]`, `{}`, `set()`
* `False`

Example:

```python
if items:  # True if items is non-empty
    process(items)
else:
    handle_empty()
```

Best practices:

* Use **explicit comparisons** when clarity matters:

  ```python
  if user is None:
      raise ValueError("User required")
  ```
* Prefer truthiness for simple “empty or not” semantics.

***

### 6. Ternary Conditional Expressions

Short inline conditional:

```python
status = "active" if is_enabled else "disabled"
```

Guidelines:

* Use only for simple assignments and expressions.
* Avoid nesting ternaries; use normal `if` blocks for complex logic.

***

### 7. `for` Loops and the Iteration Protocol

`for` loops iterate over **iterables** (lists, generators, dicts, files, etc.):

```python
for user in users:
    send_notification(user)
```

Python’s `for` loop uses the iterator protocol:

* Calls `iter(obj)` to get an iterator
* Repeatedly calls `next(iterator)` until `StopIteration`

#### 7.1 Enumerating with index

```python
for index, user in enumerate(users):
    log_index(index, user)
```

#### 7.2 Iterating over dictionaries

```python
for key, value in config.items():
    print(key, value)
```

Best practices:

* Prefer `for` over `while` for simple iteration.
* Use `enumerate`, `.items()`, `.values()`, `.keys()` instead of manual index management.

***

### 8. `while` Loops — Condition-based Repetition

`while` loops repeat while a condition remains true:

```python
retries = 0
while retries < MAX_RETRIES:
    if do_operation():
        break
    retries += 1
```

Use `while` when:

* Loop count is not fixed but depends on a condition.
* Implementing polling, retries, or state machines.

Pitfalls:

* Risk of infinite loops if the condition never becomes false.
* Use clear, visible updates to the loop condition inside the loop.

***

### 9. `break`, `continue`, and `pass`

#### 9.1 `break`

Exits the nearest enclosing loop:

```python
for user in users:
    if user.id == target_id:
        found = user
        break
```

#### 9.2 `continue`

Skips the rest of the current iteration:

```python
for user in users:
    if not user.is_active:
        continue
    process_active_user(user)
```

#### 9.3 `pass`

No-op placeholder:

```python
def future_feature():
    pass
```

Use `pass` for:

* Empty function/class definitions during development.
* Branches that must syntactically exist but will do nothing.

***

### 10. Loop `else` Clauses — Underused but Powerful

Python loops can have an `else` block:

```python
for user in users:
    if user.id == target_id:
        found = user
        break
else:
    raise LookupError("User not found")
```

Semantics:

* `else` runs only if the loop **was not terminated by `break`**.

Use cases:

* Search loops (found vs not found)
* Retry loops (success vs exhausted retries)

Avoid using `else` on loops if the team is unfamiliar with it; document its behavior clearly when used.

***

### 11. Structural Pattern Matching: `match` / `case` (Python 3.10+)

`match` introduces **structural pattern matching**, enabling expressive branching based on structure and content of values.

Basic syntax:

```python
def handle_event(event):
    match event:
        case {"type": "login", "user": user}:
            handle_login(user)
        case {"type": "logout", "user": user}:
            handle_logout(user)
        case _:
            handle_unknown(event)
```

Key benefits:

* Replaces long `if` / `elif` chains based on structure.
* Provides more declarative, readable branching.
* Works with literals, sequences, mappings, classes, and more.

***

### 12. Pattern Types in `match` / `case`

#### 12.1 Literal patterns

```python
match status:
    case "pending":
        ...
    case "approved":
        ...
    case "rejected":
        ...
```

#### 12.2 OR patterns

```python
match status:
    case "pending" | "in_review":
        queue_for_approval()
```

#### 12.3 Wildcard pattern

```python
case _:
    # default / fallback
```

#### 12.4 Sequence patterns

```python
match point:
    case [x, y]:
        handle_2d(x, y)
    case [x, y, z]:
        handle_3d(x, y, z)
```

#### 12.5 Mapping patterns

```python
match msg:
    case {"type": "error", "code": code}:
        handle_error(code)
```

#### 12.6 Class patterns

Given:

```python
class Event:
    def __init__(self, kind, payload):
        self.kind = kind
        self.payload = payload
```

You can match:

```python
match event:
    case Event(kind="login", payload=payload):
        handle_login(payload)
```

#### 12.7 Guard patterns (`if` within `case`)

```python
match user:
    case {"age": age} if age >= 18:
        allow_access()
    case _:
        deny_access()
```

Guards refine patterns with additional Boolean conditions.

***

### 13. When to Use `match` vs `if` / `elif`

Use **`if` / `elif`** when:

* You’re checking simple, independent conditions.
* Logic is straightforward and not structurally driven.

Use **`match`** when:

* Branching is primarily based on the shape and contents of a single value.
* You have many cases (like a protocol, event types, or message formats).
* You want a “switch-like” construct with richer semantics.

Guideline:

* If you have 5–10+ branches on the same value, especially with structural checks → consider `match`.

***

### 14. Control Flow and Error Handling

Control flow and error handling interplay:

* Use `if` / `match` for **expected variation** (business logic branches).
* Use exceptions (`try`/`except`) for **unexpected error** conditions.

Anti-pattern:

```python
# Using exceptions for normal control flow:
try:
    result = maybe_get_value()
except ValueError:
    result = default_value
```

Prefer:

```python
if has_value():
    result = get_value()
else:
    result = default_value
```

Use exceptions when something goes wrong, not as a normal decision branch.

***

### 15. Side-Effect Discipline in Control Flow

To keep control flow understandable:

* Separate **decision logic** from **side effects** where feasible.

Example (less ideal):

```python
if user.is_active and send_email(user):
    log("Email sent")
```

Better:

```python
if user.is_active:
    email_sent = send_email(user)
    if email_sent:
        log("Email sent")
```

This improves readability, debuggability, and testability.

***

### 16. Refactoring Nested Control Flow

Deep nesting is a smell:

```python
if condition_a:
    if condition_b:
        if condition_c:
            do_something()
```

Refactoring strategies:

1. **Guard clauses**

   ```python
   if not condition_a:
       return
   if not condition_b:
       return
   if not condition_c:
       return
   do_something()
   ```
2. **Extract functions**

   ```python
   if can_process(order):
       process(order)
   ```
3. **Use `match` for complex, structured branching**

***

### 17. Performance Considerations

Control flow itself is usually not the bottleneck; however:

* Prefer `for` loops over manually indexing lists.
* Avoid unnecessary repeated condition evaluation in hot loops.
* Use `match` judiciously; clarity matters more than micro-optimizations.
* For large data processing, prioritize algorithmic efficiency over micro-level branch tweaks.

In most production systems, clarity of control flow is more valuable than minor branch-level performance gains.

***

### 18. Common Control Flow Anti-Patterns

| Anti-Pattern                             | Problem                                       |
| ---------------------------------------- | --------------------------------------------- |
| Deeply nested `if` / `else`              | Hard to read, error-prone                     |
| Giant `if` chain on same variable        | Better expressed as `match` or dispatch table |
| Using exceptions as normal branches      | Obscures expected behavior                    |
| Unreachable branches                     | Dead code, confusion                          |
| Overuse of `break`/`continue` everywhere | Hard-to-reason-about loop behavior            |
| Hidden mutations inside condition checks | Surprise side effects                         |

***

### 19. Governance Model for Control Flow

You can think of control flow governance as:

```
Business Rule → Control Structure Choice (if / loop / match)
             → Branch Clarity (naming, simplicity)
             → Side-Effect Isolation
             → Test Coverage of Branches
             → Observability for Critical Decisions
```

Every significant decision path should be:

* Explicit
* Testable
* Explainable in business terms

***

### 20. Summary

**Python Control Flow (if, loops, match)** is the backbone of how business rules become executable logic.

Key points:

* Use `if` / `elif` / `else` for traditional branching, with simple, readable conditions.
* Use `for` for iteration over collections and `while` for condition-driven repetition.
* Use `break`, `continue`, and loop `else` to make loop behavior explicit and expressive.
* Use `match` / `case` for complex, structured branching, especially for event/message handling.
* Avoid nesting and side-effect-heavy conditions; favor clarity, refactorability, and testability.

Well-designed control flow transforms your code from “just working” into **maintainable, scalable, and auditable business logic**.

***
