✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Control Flow in Python

Control Flow in Python defines how programs execute instructions through conditional statements, loops, and functions, forming the backbone of logical program behavior.

Control flow in Python refers to the mechanisms that determine which statements execute, whether execution branches among alternatives, how blocks repeat, and when repetitive execution continues, terminates, or completes normally.


Foundations of Control Flow in Python

Control flow is the progression of execution through statements and blocks within a program. It includes:

  • Sequential execution: Running statements one after another in the order they appear.
  • Conditional selection: Choosing between alternatives based on evaluated conditions.
  • Repetition: Executing blocks repeatedly while certain conditions hold.
  • Explicit transfers: Using statements like break and continue to alter the usual flow inside loops.

Boolean conditions are expressions used as decision inputs by conditional statements and loops. These conditions undergo truth-value testing to decide whether their associated blocks execute. Importantly, truth-value testing does not require the condition to be an actual bool object; many objects have an inherent truthiness meaning whether they behave like True or False in conditionals.

Python groups statements controlled by conditions and loops into suites, which are blocks of code defined by indentation. The indentation level indicates which statements belong together under a controlling statement, differentiating block structure (the grouping of statements) from the runtime decisions that determine whether that block executes.

Control-flow conditions may include function calls, comparisons, membership tests, or other expressions. Since these evaluations can have observable side effects, decision logic should remain readable and predictable to avoid unintended consequences.

Control MechanismControl-Flow Question Answered
Sequential executionWhat statement runs next in order?
Conditional branching (if)Should this block run based on a condition?
Condition-controlled repetition (while)Should this block repeat while a condition holds?
Iterable-controlled repetition (for)What are the successive values to process?
breakShould the current loop terminate immediately?
continueShould the current iteration end early and the next iteration start?
Loop elseDid the loop complete without a break?
passShould this point do nothing but maintain syntax?
Statement 1 Condition? True path False path Loop body continue break exit loop

Conditional Execution in Python

The if statement controls execution of a suite based on the truth value of an evaluated condition. If the condition evaluates to true, the suite runs; otherwise, it is skipped.

An if and else form creates mutually exclusive alternatives selected by one condition: exactly one of the suites runs depending on the condition's truth value. This contrasts with two independent if statements, which both test conditions separately and may both run.

if, elif, and else form ordered testing of alternatives. Evaluation stops after the first condition that evaluates true, and the corresponding suite executes. If none match, the optional else suite runs.

Examples:

x = 10

# Simple if
if x > 5:
    print("x is greater than 5")

# if-else
if x % 2 == 0:
    print("x is even")
else:
    print("x is odd")

# if-elif-else chain
if x < 0:
    print("x is negative")
elif x == 0:
    print("x is zero")
else:
    print("x is positive")

Nested conditionals involve placing one if inside another. This can be compared with compound Boolean conditions using logical operators. The choice depends on whether the later decision conceptually depends on the earlier one.

Example of nested and flattened:

# Nested
if x > 0:
    if x < 10:
        print("x is positive and less than 10")

# Flattened
if 0 < x < 10:
    print("x is positive and less than 10")

Both print under the same condition, but nesting emphasizes conceptual dependency.

Guard-style early decisions handle invalid or exceptional cases before the main computation path:

if x < 0:
    print("Invalid: x must be non-negative")
else:
    # Main logic here
    print("Processing x =", x)

The match statement directs conditional dispatch based on pattern matching, differing from an if chain by matching structures or values explicitly.

Example comparing if/elif and match:

value = 'b'

# if-elif
if value == 'a':
    print("Matched a")
elif value == 'b':
    print("Matched b")
else:
    print("No match")

# match-case
match value:
    case 'a':
        print("Matched a")
    case 'b':
        print("Matched b")
    case _:
        print("No match")

Common mistakes in conditionals include overlapping conditions, unreachable branches, incorrect order of numeric boundaries, unintended truthiness (e.g., empty sequences), repeated expensive expressions, and side effects that obscure logic.

Example of incorrect boundary ordering:

x = 75

# Incorrect order
if x > 50:
    print("More than 50")
elif x > 80:
    print("More than 80")
else:
    print("50 or less")

Output will be "More than 50" for x = 75, so the x > 80 branch is never reached. Correct order:

if x > 80:
    print("More than 80")
elif x > 50:
    print("More than 50")
else:
    print("50 or less")

This ensures mutually exclusive and correctly ordered classification.


While Loops in Python

A while loop repeatedly executes its body as long as a condition evaluates to true before each iteration.

The loop’s state is maintained through variables whose values change across iterations. Initialization sets starting values; the condition evaluates current state; the body executes; state updates prepare for the next iteration. Eventually, the condition becomes false, terminating the loop.

Example:

count = 0  # Initialization
while count < 5:  # Condition
    print("Count is", count)  # Body
    count += 1  # State update

Common patterns:

  • Counter-controlled loops: Use a numeric counter variable and terminate when it reaches a limit.
  • Sentinel-controlled loops: Continue until a special sentinel value is encountered.
  • State-controlled loops: Terminate based on more complex state conditions.

Examples:

# Counter-controlled
n = 3
i = 0
while i < n:
    print("Iteration", i)
    i += 1

# Sentinel-controlled (simulated input)
inputs = [10, 20, -1, 30]
index = 0
while True:
    value = inputs[index]
    if value == -1:
        break
    print("Value:", value)
    index += 1

An infinite loop occurs when the condition never becomes false given reachable state changes. Some infinite loops serve as indefinite services; others are errors.

Progress conditions and loop invariants help reason about loops. The progress condition is the state change moving toward termination; the loop invariant is a property true before and after each iteration.

Example of a faulty loop:

x = 0
while x < 5:
    print(x)
    # Missing update: x is never incremented, loop never ends

Corrected:

x = 0
while x < 5:
    print(x)
    x += 1  # Progress ensures eventual termination

Use a while loop when repetition depends on evolving state or conditions not tied solely to consuming an iterable.


For Loops in Python

A Python for loop executes repeatedly by obtaining successive values from an iterable and binding each to the loop target before executing the body.

Loop-target binding can be a simple variable or structured unpacking. Each iteration binds the next value as if by assignment.

Examples:

# List iteration
for item in [1, 2, 3]:
    print(item)

# String iteration
for ch in "abc":
    print(ch)

# Dictionary keys
d = {'a': 1, 'b': 2}
for key in d:
    print(key, d[key])

# Range iteration
for i in range(3):
    print(i)

# Structured unpacking of pairs
pairs = [(1, 'a'), (2, 'b')]
for number, letter in pairs:
    print(number, letter)

range provides integers for counted repetition, but Python for loops are not limited to numeric iteration.

Examples:

# Counted loop
for i in range(5):
    print(i)

# Value-oriented loop
fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
    print(fruit)

Since for loops iterate directly over elements, manufacturing indices is unnecessary when only elements are needed.

enumerate provides both element and position:

for index, fruit in enumerate(fruits):
    print(index, fruit)

This contrasts with manually maintaining a separate counter.

Parallel iteration with zip pairs values from multiple iterables, terminating at the shortest:

names = ['Alice', 'Bob']
ages = [30, 25]
for name, age in zip(names, ages):
    print(name, age)

Detecting mismatched lengths requires explicit checks or using itertools.zip_longest with validation.

Mutating a collection while iterating over it is risky, as it can change iteration behavior unpredictably.

Unsafe example:

numbers = [1, 2, 3, 4]
for n in numbers:
    if n % 2 == 0:
        numbers.remove(n)  # Modifies list during iteration
print(numbers)  # Unexpected result

Safe alternative:

numbers = [1, 2, 3, 4]
filtered = [n for n in numbers if n % 2 != 0]
print(filtered)

or iterating over a copy:

numbers = [1, 2, 3, 4]
for n in numbers[:]:
    if n % 2 == 0:
        numbers.remove(n)
print(numbers)

Python Loop Control

break immediately terminates the nearest enclosing loop; execution continues after the loop.

continue terminates the current iteration early and starts the next iteration according to the loop’s mechanism.

Examples showing difference:

# break example
for i in range(5):
    if i == 3:
        break
    print(i)
# Output: 0 1 2

# continue example
for i in range(5):
    if i == 3:
        continue
    print(i)
# Output: 0 1 2 4

The else clause on for and while loops runs if the loop completes without encountering a break. It is not an else paired with the loop condition.

Example:

for i in range(5):
    if i == 3:
        print("Found 3, breaking")
        break
else:
    print("3 not found")

# Output:
# Found 3, breaking

If the loop never found 3, the else would execute.

pass is a null statement performing no control transfer, different from continue which changes loop progression.

break and continue affect only the nearest enclosing loop, not multiple levels automatically.

Nested loop example:

for i in range(3):
    for j in range(3):
        if j == 1:
            break  # breaks inner loop only
        print(i, j)

To break from multiple loops, use flags, functions, or reorganize logic:

done = False
for i in range(3):
    for j in range(3):
        if j == 1:
            done = True
            break
        print(i, j)
    if done:
        break

Composing Conditional and Repetitive Control Flow

Conditions and loops combine to implement filtering, searching, validation, accumulation, retries, and state transitions. Keeping conditions and termination explicit improves clarity.

Example:

records = [{'id': 1, 'valid': True, 'value': 10},
           {'id': 2, 'valid': False, 'value': 20},
           {'id': 3, 'valid': True, 'value': 30}]

total = 0
threshold = 35
normal_exit = True

for record in records:
    if not record['valid']:
        continue  # Skip invalid records
    total += record['value']
    if total >= threshold:
        normal_exit = False
        break  # Early exit on threshold reached

if normal_exit:
    print("Processed all records, total:", total)
else:
    print("Stopped early, total:", total)

Excessive nesting, duplicated conditions, hidden state changes, and multiple unrelated exits make control flow hard to follow. Simplifications include flattening nested conditions, consolidating repeated checks, making state changes explicit, and unifying exit points.


Solved Control Flow Exercises in Python

Exercise: Process a sequence of numeric values, reject invalid values, classify acceptable values, accumulate counts, skip certain values, and stop early on aggregate conditions.

values = [12, -3, 45, 67, 23, 89, 5, 100, 42, -1, 55]
count_small = 0
count_medium = 0
count_large = 0
max_total = 150
total = 0

for v in values:
    if v < 0:
        # Reject invalid
        continue
    if v < 20:
        count_small += 1
    elif v < 50:
        count_medium += 1
    else:
        count_large += 1
    total += v
    if total > max_total:
        break  # Early termination

print(f"Small: {count_small}, Medium: {count_medium}, Large: {count_large}, Total: {total}")

Step-by-step:

  • Invalid values (v < 0) are skipped.
  • Values classified into small, medium, or large using ordered branches.
  • Counters accumulate category counts.
  • The total sum accumulates the values processed.
  • Early termination occurs when total exceeds max_total.
  • Final print reports counts and total.

Boundary cases include negative values skipped and values exactly on category boundaries.

Second exercise: while loop with nested for searching candidates.

target = 50
max_attempts = 5
attempt = 0
found = False

while attempt < max_attempts:
    for candidate in range(45, 55):
        if candidate == target:
            found = True
            break
    if found:
        break
    attempt += 1
else:
    print("Target not found after all attempts")

if found:
    print(f"Target {target} found in attempt {attempt + 1}")
else:
    print("Search unsuccessful")

This example shows:

  • A while loop controlling multiple attempts.
  • A nested for loop searching candidates.
  • Use of break to exit inner and outer loops.
  • The loop else clause executing only if the while loop completes without break.
  • Correct termination in both success and failure cases.