Skip to content

2.1.5 Flow Control

Python flow control execution path diagram

In this section, you will learn how to make a program “make decisions” and “repeat actions.” Conditional logic and loops are the backbone of all automation scripts, data processing pipelines, and model training code. Once you understand them, your code will no longer just execute from top to bottom.

  • Master if/elif/else conditional logic
  • Master for loops and while loops
  • Learn to use break and continue to control loops
  • Be able to write programs with nested logic

So far, the code you have written has all been executed line by line from top to bottom. But real programs need to make decisions and repeat actions — that is flow control.

Imagine the decision process when you leave home in the morning:

If it is raining:
take an umbrella
Otherwise if the sun is very strong:
wear a hat
Otherwise:
just leave

This is conditional logic.

Now imagine memorizing vocabulary words:

Repeat 100 times:
look at a new word
remember it

This is a loop.


failed_tests = 3
if failed_tests > 0:
print("Stop the release and inspect the failing tests.")

Syntax rules:

  1. if is followed by a condition expression
  2. The condition must end with a colon : (many beginners forget this)
  3. The code that runs when the condition is true must be indented by 4 spaces
all_checks_passed = False
if all_checks_passed:
print("Build is ready to deploy")
print("Write the release note")
else:
print("Keep the build in review")
print("Fix the failing checks first")

elif is short for “else if” and is used to check multiple conditions:

latency_ms = 185
if latency_ms < 100:
status = "Fast"
elif latency_ms < 200:
status = "Healthy"
elif latency_ms < 500:
status = "Slow"
else:
status = "Critical"
print(f"API latency: {latency_ms} ms, status: {status}")
# Output: API latency: 185 ms, status: Healthy
# Ternary expression (one line for a simple if-else)
latency_ms = 185
status = "Within budget" if latency_ms <= 200 else "Needs review"
print(status) # Within budget
# Equivalent to:
if latency_ms <= 200:
status = "Within budget"
else:
status = "Needs review"

You can put conditions inside other conditions:

has_approval = True
all_tests_passed = False
if has_approval:
if all_tests_passed:
print("Deploy the build")
else:
print("Wait for the test suite to pass")
else:
print("Ask for release approval first")

However, too many levels of nesting make code hard to read, so it is usually not recommended to go beyond 3 levels.


A for loop is used to iterate over each element in a sequence (lists, strings, ranges, etc.).

services = ["Login API", "Search API", "Worker", "Dashboard"]
for service in services:
print(f"Checking {service}")
# Output:
# Checking Login API
# Checking Search API
# Checking Worker
# Checking Dashboard

The idea is: for service in services means “for each service in services, execute the code below.”

word = "Python"
for char in word:
print(char, end=" ")
# Output: P y t h o n

range() generates a sequence of numbers and is the most common partner for for loops:

# range(5) generates 0, 1, 2, 3, 4
for i in range(5):
print(i, end=" ")
# Output: 0 1 2 3 4
# range(start, stop) goes from start to stop-1
for i in range(1, 6):
print(i, end=" ")
# Output: 1 2 3 4 5
# range(start, stop, step) with a step size
for i in range(0, 10, 2):
print(i, end=" ")
# Output: 0 2 4 6 8
# Count down
for i in range(5, 0, -1):
print(i, end=" ")
# Output: 5 4 3 2 1
total_minutes = 0
for day in range(1, 6):
total_minutes += 30
print(f"Review minutes for 5 days: {total_minutes}") # 150
tasks = ["Design login form", "Build API endpoint", "Write smoke test"]
# Traditional way
for i in range(len(tasks)):
print(f"Task {i+1}: {tasks[i]}")
# More Pythonic way: use enumerate
for i, task in enumerate(tasks):
print(f"Task {i+1}: {task}")
# Specify the starting number
for i, task in enumerate(tasks, start=1):
print(f"Task {i}: {task}")

A while loop keeps running as long as the condition is true, and stops when the condition becomes false.

count = 0
while count < 5:
print(f"Current count: {count}")
count += 1 # Don't forget to update the condition!
print("Loop ended")
# Output:
# Current count: 0
# Current count: 1
# Current count: 2
# Current count: 3
# Current count: 4
# Loop ended

while is suitable when the number of iterations is unknown:

# Scenario: wait for a background job to finish
job_status = "queued"
poll_count = 0
while job_status != "finished":
poll_count += 1
print(f"Poll {poll_count}: {job_status}")
if poll_count == 1:
job_status = "running"
elif poll_count == 2:
job_status = "finished"
print(f"Job finished after {poll_count} polls")
ScenarioRecommendedReason
Iterate over a list/stringforNaturally suited
Loop a fixed number of timesfor + range()Clear and concise
Unknown number of iterationswhileFlexible control
Wait until a condition is metwhileIntuitive

Rule of thumb: use for whenever possible; it is safer (it won’t become an infinite loop).


# Stop as soon as the first slow request is found
latencies_ms = [120, 145, 310, 180, 260]
for latency_ms in latencies_ms:
if latency_ms > 250:
print(f"First slow request: {latency_ms} ms")
break
print(f"{latency_ms} ms is within range, keep checking...")
# Output:
# 120 ms is within range, keep checking...
# 145 ms is within range, keep checking...
# First slow request: 310 ms

continue: skip the current iteration and move to the next one

Section titled “continue: skip the current iteration and move to the next one”
# Print only slow requests, skip healthy ones
latencies_ms = [95, 210, 180, 260, 130]
for latency_ms in latencies_ms:
if latency_ms <= 200:
continue # Skip healthy requests
print(latency_ms, end=" ")
# Output: 210 260
# break: leave the loop immediately
for i in range(10):
if i == 5:
break # The loop stops completely at 5
print(i, end=" ")
# Output: 0 1 2 3 4
# continue: skip the current item and go to the next one
for i in range(10):
if i == 5:
continue # Skip 5 and continue with 6, 7, 8, 9
print(i, end=" ")
# Output: 0 1 2 3 4 6 7 8 9

Python loops have a unique else clause — it runs when the loop ends normally (that is, not stopped by break):

# Check whether a required review is missing
completed_checks = ["unit-test", "lint", "api-test"]
required_check = "security-review"
for check in completed_checks:
if check == required_check:
print(f"{required_check} is complete")
break
else:
# The loop was not terminated by break, so the required check was not found
print(f"{required_check} is missing")
# Output: security-review is missing

You can put a loop inside another loop:

# Print a module/check matrix
modules = ["API", "UI", "DB"]
checks = ["lint", "test"]
for module in modules:
for check in checks:
print(f"{module}:{check}", end="\t")
print() # New line after each module

Output:

API:lint API:test
UI:lint UI:test
DB:lint DB:test

Example 1: Simulate an AI model training process

Section titled “Example 1: Simulate an AI model training process”
import random
print("=== Starting model training ===")
print(f"{'Epoch':<10}{'Loss':<15}{'Accuracy':<15}{'Status'}")
print("-" * 50)
loss = 2.5
accuracy = 0.10
for epoch in range(1, 21):
# Simulate training: loss gradually decreases, accuracy gradually increases
loss *= random.uniform(0.85, 0.95)
accuracy = min(accuracy + random.uniform(0.03, 0.06), 1.0)
# Determine training status
if accuracy >= 0.95:
status = "✅ Achieved"
elif accuracy >= 0.80:
status = "📈 Good"
else:
status = "🔄 Training"
print(f"{epoch:<10}{loss:<15.4f}{accuracy:<15.2%}{status}")
# Stop early if accuracy reaches 98%
if accuracy >= 0.98:
print(f"\nEarly stopping! Target accuracy reached at epoch {epoch}")
break
else:
print(f"\nTraining complete! Final accuracy: {accuracy:.2%}")
password = input("Please enter a password: ")
has_upper = False # Contains an uppercase letter
has_lower = False # Contains a lowercase letter
has_digit = False # Contains a digit
has_special = False # Contains a special character
for char in password:
if char.isupper():
has_upper = True
elif char.islower():
has_lower = True
elif char.isdigit():
has_digit = True
else:
has_special = True
# Calculate strength score
score = 0
if len(password) >= 8:
score += 1
if has_upper:
score += 1
if has_lower:
score += 1
if has_digit:
score += 1
if has_special:
score += 1
# Output result
print(f"\nPassword strength: {'' * score}{'' * (5 - score)} ({score}/5)")
if score <= 2:
print("Weak password! Consider strengthening it")
elif score <= 4:
print("Medium strength")
else:
print("Strong password!")

Practice branch order with a small release-check labeler:

Print sample numbers from 1 to 50, but:

  • If a number is divisible by 15, print “FullCheck”
  • If a number is divisible by 3, print “Lint”
  • If a number is divisible by 5, print “Test”
  • Otherwise, print the number itself
for i in range(1, 51):
if i % 15 == 0:
print("FullCheck")
elif i % 3 == 0:
print("Lint")
elif i % 5 == 0:
print("Test")
else:
print(i)

Hint: First check whether the number is divisible by 15, then check 3 and 5 separately.

Exercise 2: Latency Alert Loop (Limited Samples)

Section titled “Exercise 2: Latency Alert Loop (Limited Samples)”

Check at most 7 latency samples and stop as soon as one exceeds the threshold.

latencies_ms = [120, 180, 260, 140, 310, 190, 170]
threshold_ms = 250
max_samples = 7
for sample_no, latency_ms in enumerate(latencies_ms[:max_samples], start=1):
print(f"Sample {sample_no}: {latency_ms} ms")
if latency_ms <= threshold_ms:
print("Healthy")
continue
print("Alert: latency exceeded the threshold")
break
else:
print("All checked samples stayed within the threshold.")

Exercise 3: Print a Deployment Progress Bar

Section titled “Exercise 3: Print a Deployment Progress Bar”

Use loops to print the following progress shape:

#
##
###
####
#####

Then try printing a countdown progress bar:

#####
####
###
##
#

Print every check name whose status is not "passed".

checks = [
("lint", "passed"),
("unit-test", "failed"),
("api-test", "passed"),
("security-review", "failed"),
]
for check_name, status in checks:
if status == "passed":
continue
print(f"{check_name}: {status}")
Reference implementation and walkthrough
  1. Release check labels should test divisibility by 15 first. Otherwise 15 may print Lint or Test too early.
  2. For the latency list, test the healthy path, alert path, and all-clear path by moving or removing the slow value.
  3. Progress bars can be printed with for n in range(1, 6): print("#" * n) and a reversed range for the countdown.
  4. Failed-check filtering should use continue for "passed" and only print failed or otherwise non-passing statuses.
  5. Watch off-by-one errors: range(1, 51) includes 50; range(1, 50) does not.

Keep this page’s proof of learning as a small evidence card:

Concept
variable, type, operator, input/output, branch, loop, structure, function, or module
Code
smallest runnable Python snippet for the concept
Output
printed value, type, branch result, loop trace, or returned value
Failure Check
type mismatch, indentation, off-by-one, mutable data, or import path issue
Expected Output
code plus printed result that proves the concept works
SyntaxPurposeKey Point
if/elif/elseConditional logicConditions are checked from top to bottom; don’t forget the colon and indentation
for...inIterate over a sequenceUsed with range(), lists, and strings
whileConditional loopUpdate the condition to avoid infinite loops
breakStop the loopExit the entire loop immediately
continueSkip this iterationSkip the current iteration and move to the next one
range()Generate a sequence of numbersrange(start, stop, step)