---
title: 'Conditional Branching'
source: 'https://academia.sh/en/courses/programming-fundamentals/conditional-branching'
course: 'Programming Fundamentals'
language: en
updated: '2026-08-17T18:08:23+00:00'
license: 'CC BY-SA 4.0'
---

# Conditional Branching

Condition-dependent execution, multi-way selection, writing readable conditions, and mapping-based dispatch.

The programs written so far have followed a single path from start to end: each
statement executed once, in its turn. Most problems, however, require different
behavior depending on the situation. If a measurement is above a limit, a warning
should be given; if it is below, nothing should happen.

This lesson covers how flow splits based on a condition. The previous course showed
its hardware counterpart: a comparison instruction sets flags, and a conditional
branch instruction changes the program counter only if the condition holds. Every
construct here ultimately reduces to those instructions.

## One-Way and Two-Way Selection

The simplest form is a block that executes only when a condition holds:

```python
measurement = 25
LIMIT = 20

if measurement > LIMIT:
    print("limit exceeded")      # limit exceeded
```

A condition is any expression that produces a logical value. If it does not hold, the
block is skipped and flow continues below it.

In two-way selection, a block to run when the condition does not hold is also defined:

```python
if measurement > LIMIT:
    status = "high"
else:
    status = "normal"

print(status)                    # high
```

Which statements a block covers is shown differently by language: some use curly
braces, some close the block with a keyword, Python uses indentation. The notation
varies; the meaning is the same — the set of statements to execute depending on the
condition is marked off.

In languages that use indentation, one warning applies: indentation is not mere
formatting but meaning. A statement believed to be inside the block but whose
indentation has shifted always runs regardless of the condition. This is a common
instance of the logic error class defined in the first lesson.

## Multi-Way Selection

When there are more than two cases, conditions are chained. In the chain, **the first
condition that holds wins**; the rest are never tested.

```python
def classify(measurement: int) -> str:
    if measurement < 10:
        return "low"
    elif measurement < 20:
        return "medium"
    elif measurement < 30:
        return "high"
    else:
        return "extreme"

print(classify(7), classify(12), classify(25), classify(40))
# low medium high extreme
```

The order of the chain determines the meaning of the conditions. The second branch's
condition `measurement < 20`, read on its own, is also true for the value $5$; but by
the time it is reached, the first condition is known not to hold, meaning
`measurement` is at least $10$. Every branch implicitly carries the negation of all
the ones before it.

This is why reordering breaks the program. The following chain always produces
"extreme", because its first condition is true for nearly every value:

```python
def classify_wrong(measurement: int) -> str:
    if measurement >= 10:        # broad condition placed first
        return "extreme"
    elif measurement < 20:
        return "medium"
    else:
        return "low"

print(classify_wrong(12))        # extreme  — expected "medium"
print(classify_wrong(25))        # extreme  — correct for this value
```

The practical rule: conditions are ordered from the narrowest case to the broadest;
the most general case is collected in the `else` branch. If the chain has no `else`
branch at the end, input that satisfies no condition passes through silently, doing
nothing — this is where overlooked cases typically hide. The last branch of every
chain should state explicitly what happens for unexpected input.

## Writing Readable Conditions

As a condition grows more complex, code readability drops quickly. Three habits
prevent this.

**Using the logical value directly.** Writing `if condition == True` is unnecessary;
`condition` is already a logical value. Adding the comparison lengthens the expression
and risks accidentally turning into an assignment.

**Naming the condition.** A long condition is bound to a variable that states its
meaning:

```python
measurement, temperature = 25, 80

# Hard to read:
if measurement > 20 and temperature > 75 and not (measurement > 40):
    print("warning")

# Named:
limit_exceeded = measurement > 20
too_hot = temperature > 75
within_safe_range = measurement <= 40

if limit_exceeded and too_hot and within_safe_range:
    print("warning")             # warning
```

**Simplifying negation.** Nested negations are flattened using De Morgan's laws: the
expression `not (a and b)` is equivalent to `(not a) or (not b)`. Which of the two
forms to choose is decided by which one states the problem more directly.

## The Conditional Expression

Conditional branching is a statement: it performs an action, it does not produce a
value. Many languages, by contrast, also offer a form that **produces a value**
depending on a condition. The difference between the two forms is a direct
application of the statement–expression distinction from the first lesson.

```python
measurement = 25

# Statement form: the status variable is bound separately in each branch.
if measurement > 20:
    status = "high"
else:
    status = "normal"

# Expression form: a single binding, the condition selects the value.
status = "high" if measurement > 20 else "normal"
print(status)                    # high
```

The expression form improves readability in short cases where a value is bound to one
of two options: the variable is written once, and the binding is visibly in a single
place.

It also has a limit. If the branches are long, nested, or contain side effects, the
expression form becomes unreadable. The criterion is this: if the condition selects a
**value**, use the expression form; if it selects an **action**, use the statement
form.

## Nested Conditions

Conditions can be nested inside one another. As depth grows, reading gets harder; each
level adds an assumption the reader must keep in mind.

Condition stacks more than three levels deep are usually a sign that two separate
problems are being solved in the same place. The standard way to reduce depth is to
rule out invalid cases first and exit early. This pattern is revisited under the name
**guard clause** in the functions topic; its counterpart here is turning nested
branches into a flat chain.

## Mapping-Based Dispatch

If a condition chain is nothing but a single value compared against constants, many
languages offer a dedicated construct for it — switch-based selection or pattern
matching. The construct's name and capabilities vary by language.

An approach that offers an alternative to the chain is moving the selection into data:

```python
UNIT_FACTOR = {"mm": 0.001, "cm": 0.01, "m": 1.0}

def to_meters(value: float, unit: str) -> float:
    if unit not in UNIT_FACTOR:
        raise ValueError(f"unknown unit: {unit}")
    return value * UNIT_FACTOR[unit]

print(to_meters(25, "cm"))       # 0.25
print(to_meters(1500, "mm"))     # 1.5
```

This form's advantage is that adding a new unit is a data change, not a code change. A
condition chain grows longer with every new case; a mapping table stays the same.
This difference becomes more pronounced as the number of decisions grows; in the
software design curriculum, the same idea is expanded under the heading of replacing
conditionals with polymorphism.

The selection criterion is this: if the cases are fixed and few, a condition chain is
clear; if the cases grow or change at run time, a mapping is preferred.

## Summary

- Conditional branching executes a block of code only when a condition holds; a
  condition is an expression that produces a logical value.
- Block boundaries are shown differently by language; in languages that use
  indentation, indentation is part of the meaning.
- In multi-way selection, the first condition that holds wins; every branch implicitly
  carries the negation of the ones before it, and reordering breaks the program.
- Conditions are made readable by naming them and simplifying negations; a logical
  value is not compared with `== True`.
- When a single value is compared against constants, a mapping table can replace a
  growing condition chain.

## Next Step

Branching determines which path flow takes, but it executes each path only once. The
measurement sequence in the shared problem, however, requires the same work for every
element. The next lesson covers loops, which execute the same block repeatedly, and
the guarantee that a loop terminates.
