Chapter 4 Exercises: Control Flow — Making Decisions in Your Programs

These exercises are organized into five tiers. Complete them in order — later tiers build on skills from earlier ones.

How to use these exercises: - Tier 1 and 2 exercises can be completed in a Python file or directly in the Python REPL. - Tier 3 exercises require careful reading and analysis before writing code. - Tier 4 exercises should be completed as standalone .py files with sensible test data included. - Tier 5 exercises are open-ended design challenges. There is no single correct answer.


Tier 1: Recall — Basic Syntax and Concepts

These exercises check that you can use the core syntax from memory.


Exercise 1.1 — Your First Business Condition

Write a Python script that stores an invoice amount in a variable and prints a message based on whether it is overdue.

Setup:

invoice_amount = 1250.00
days_overdue = 0   # Change this to test

Requirements: - If days_overdue is greater than 60, print: "URGENT: Invoice is seriously overdue." - If days_overdue is between 31 and 60 (inclusive), print: "NOTICE: Invoice is overdue." - If days_overdue is between 1 and 30 (inclusive), print: "REMINDER: Invoice will be due soon." - If days_overdue is 0 or negative, print: "Invoice is current."

Test your script with days_overdue values of 0, 15, 45, and 75.

What to practice: if/elif/else, comparison operators, f-strings.


Exercise 1.2 — Shipping Cost Calculator

Acme Corp charges shipping based on destination zone:

Zone Cost
Zone A (local) $5.99
Zone B (regional) $12.99
Zone C (national) $19.99
International $49.99
Unknown $25.00 (default)

Write a script that takes a zone variable (a string) and prints the correct shipping cost.

Bonus: Use match/case instead of if/elif.


Exercise 1.3 — Truthiness Quiz

For each of the following values, predict whether Python will evaluate it as truthy or falsy. Then verify by running: bool(value) in the Python REPL.

values = [
    0,
    1,
    -1,
    0.0,
    0.001,
    "",
    "False",
    "0",
    [],
    [0],
    {},
    {"key": None},
    None,
    False,
    True,
]

After checking, write a one-sentence explanation for each value you found surprising.


Exercise 1.4 — and / or / not Practice

Predict the output of each expression without running it first, then verify:

# Expression 1
print(True and False)

# Expression 2
print(True or False)

# Expression 3
print(not True)

# Expression 4
print(5 > 3 and 10 < 20)

# Expression 5
print("" or "default_value")

# Expression 6
print("hello" and "world")

# Expression 7
x = None
print(x or "fallback")

# Expression 8
inventory = []
print("In stock" if inventory else "Out of stock")

Write a comment next to each expression explaining why Python produces that output.


Exercise 1.5 — Fix the Syntax Errors

The following code contains five syntax errors. Find and fix all of them:

discount_rate = 0.10
order_total = 500

if order_total > 1000
    discount = order_total * discount_rate
    print(f"Discount applied: ${discount:.2f}")
  elif order_total > 500:
        discount = order_total * 0.05
        print(f"Small discount: ${discount:.2f}")
else
    print("No discount.")
    discount = 0

final_total = order_total - discount
print("Final total: $final_total")

List each error and explain what was wrong.


Tier 2: Apply — Writing Business Logic

These exercises ask you to translate realistic business rules into working code.


Exercise 2.1 — Employee Overtime Calculator

Write a function calculate_weekly_pay(hours_worked, hourly_rate, employee_type) that follows these rules:

  • For employee_type == "hourly":
  • First 40 hours: regular rate
  • Hours 41–50: 1.5× rate
  • Hours above 50: 2× rate
  • For employee_type == "salaried":
  • Weekly pay is always hourly_rate * 40 regardless of hours (no overtime)
  • For any other employee_type: print a warning and return 0.0

The function should return the total weekly pay as a float.

Test cases:

calculate_weekly_pay(45, 22.00, "hourly")   # 40×22 + 5×33 = $1045
calculate_weekly_pay(55, 18.00, "hourly")   # 40×18 + 10×27 + 5×36 = $1170
calculate_weekly_pay(60, 30.00, "salaried") # Always 40×30 = $1200
calculate_weekly_pay(40, 25.00, "contract") # Warning + 0.0

Exercise 2.2 — Lead Qualification Screener

Maya is working with a client who needs to qualify sales leads. A lead is:

  • Hot if: budget > $50,000 AND timeline <= 3 months AND decision_maker is identified
  • Warm if: budget is between $20,000 and $50,000 OR (budget > $50,000 but missing decision_maker or timeline > 3 months)
  • Cold if: budget < $20,000 OR no contact information provided

Write a function qualify_lead(budget, timeline_months, has_decision_maker, has_contact_info) that returns the lead category as a string.


Exercise 2.3 — Product Pricing by Customer Segment

A SaaS product charges different prices based on customer segment:

  • Enterprise: $150/seat/month, minimum 20 seats, volume discount 15% for 50+ seats
  • Business: $75/seat/month, minimum 5 seats, volume discount 10% for 25+ seats
  • Startup: $35/seat/month, no minimum, no volume discount
  • Individual: $15/seat/month, maximum 1 seat

Write calculate_monthly_bill(segment, num_seats) that: 1. Validates that the seat count meets the minimum (and doesn't exceed maximum for Individual) 2. Applies the volume discount if applicable 3. Returns the monthly total


Exercise 2.4 — Expense Report Approval Routing

Acme Corp routes expense reports based on amount:

Amount Action
Under $100 Auto-approved, no receipt required
$100–$499 Auto-approved, receipt required
$500–$1,999 Manager approval required
$2,000–$9,999 VP approval required
$10,000+ CFO approval + board notification

Write a function route_expense_report(amount, has_receipt, employee_level) that: - Returns the required approval level - Adds a warning if has_receipt is False but a receipt is required - Gives a bypass for employee_level == "executive" — executives auto-approve up to $5,000


Exercise 2.5 — Match/Case: Order Status Handler

An order can be in one of these statuses: "pending", "confirmed", "picked", "packed", "shipped", "delivered", "cancelled", "returned".

Using match/case, write a function describe_order_status(status) that returns a customer-friendly message for each status. Include a wildcard case for unknown statuses.


Tier 3: Analyze — Reasoning About Code

These exercises require reading code carefully and thinking through edge cases.


Exercise 3.1 — Trace the Execution

Given this code:

def classify_account(balance, months_open, late_payments):
    if months_open < 3:
        return "New"

    if balance < 0:
        return "Overdrawn"

    if late_payments > 5:
        if balance < 1000:
            return "High Risk"
        else:
            return "Watch"
    elif late_payments > 2:
        return "Caution"
    elif balance >= 50_000:
        return "Premium"
    else:
        return "Standard"

For each of the following inputs, trace through the code step by step and determine what is returned without running it:

a) classify_account(25000, 1, 0) → ? b) classify_account(-500, 24, 3) → ? c) classify_account(75000, 36, 6) → ? d) classify_account(500, 24, 6) → ? e) classify_account(75000, 36, 1) → ?

After tracing, run the code and compare your predictions.


Exercise 3.2 — Short-Circuit Analysis

For each pair, determine whether both conditions are evaluated or whether Python short-circuits:

customer = None

# Statement 1
if customer and customer.credit_score > 700:
    print("Good credit")

# Statement 2
if customer or create_default_customer():
    print("Customer available")

# Statement 3
result = customer and customer.name or "Unknown Customer"

# Statement 4
emails = []
primary_contact = emails[0] if emails else "no-reply@acme.com"

For each: 1. Does Python short-circuit, and at which point? 2. What is the value of any variable assigned? 3. What would happen if customer were not None but a valid object?


Exercise 3.3 — Identify the Bug

Each snippet below has a logical error (not a syntax error — it runs, but produces wrong results). Identify and fix each one.

Bug A:

# Intended: apply discount only to Gold OR Silver customers with orders > $1000
customer_tier = "Bronze"
order_value = 1500

if customer_tier == "Gold" or "Silver" and order_value > 1000:
    discount = 0.10
else:
    discount = 0

Bug B:

# Intended: flag invoices that are between 30 and 60 days overdue
days_overdue = 45

if 30 < days_overdue < 60:
    status = "Overdue"
elif days_overdue == 30 or days_overdue == 60:
    status = "Boundary"
else:
    status = "Not overdue"

# What happens with days_overdue = 60? Is that the intended behavior?

Bug C:

# Intended: use None to mean "rate not set yet" and 0.0 to mean "no commission"
commission_rate = 0.0

if not commission_rate:
    print("Commission rate has not been set. Using default 5%.")
    commission_rate = 0.05

Bug D:

# Intended: approve if ALL three conditions are met
credit_score = 750
income = 80_000
employment_status = "employed"

is_approved = credit_score > 700 or income > 60_000 or employment_status == "employed"

Exercise 3.4 — The Arrow of Doom

The following function works correctly but is difficult to read and maintain:

def process_payment(amount, card_type, is_expired, has_funds, is_fraud_flagged):
    if not is_expired:
        if not is_fraud_flagged:
            if has_funds:
                if amount > 0:
                    if card_type in ["Visa", "Mastercard", "Amex"]:
                        return "Payment approved"
                    else:
                        return "Unsupported card type"
                else:
                    return "Invalid amount"
            else:
                return "Insufficient funds"
        else:
            return "Card flagged for fraud"
    else:
        return "Card is expired"

Rewrite this function using guard clauses so the happy path (payment approval) is at the end with minimal nesting. Keep all the same logic and return values.


Exercise 3.5 — Truthiness Edge Cases

What does each of the following produce? Explain the result:

# Case 1
print(0 or False or None or "" or [] or "finally")

# Case 2
print(1 and 2 and 3 and 4)

# Case 3
print([] or {} or () or 0 or None)

# Case 4
name = input("Enter name: ")   # User presses Enter without typing anything
greeting = f"Hello, {name or 'Guest'}!"
print(greeting)

# Case 5
discount = None
applied = discount or 0
print(f"Discount applied: {applied * 100:.1f}%")   # Does this crash?

Tier 4: Synthesize — Building Complete Systems

These exercises require designing and building a complete decision system from scratch.


Exercise 4.1 — Employee Leave Request Approver

Build a complete leave request evaluation system for Acme Corp. The system should evaluate requests based on:

Input data (per request): - employee_name (str) - leave_type ("vacation", "sick", "personal", "bereavement", "unpaid") - days_requested (int) - days_balance_available (float — current PTO balance) - months_employed (int) - team_coverage_available (bool — whether someone can cover their work) - days_notice_given (int — how many days in advance they are requesting)

Policy rules to encode: 1. Sick leave: always approved regardless of notice, up to 10 days per year 2. Bereavement: always approved, up to 5 days, immediate start allowed 3. Vacation: requires 5 days notice, must have sufficient balance, auto-approved up to 5 days; 6-10 days requires manager review; over 10 days requires VP approval 4. Personal: 1 day notice required, balance must cover it, max 3 days per request 5. Unpaid: always escalated to HR, no auto-approval

Return a decision with: status ("APPROVED", "PENDING REVIEW", "ESCALATED", "DENIED"), approver, and reason.

Deliverable: A Python file with the evaluation function and at least 6 test cases covering different branches.


Exercise 4.2 — Pricing Engine with Precedence Rules

Build a product pricing engine that applies discounts in a specific order of precedence:

  1. Contract price — if the customer has a negotiated contract price, use it (no other discounts apply)
  2. Promotional override — if a product is on promotion, use the promotional price (no tier discount applies, but volume discounts still do)
  3. Tier + volume discount — apply tier discount first, then volume discount on the result

Additional rules: - Volume discounts: 2% for 10–24 units, 5% for 25–49 units, 8% for 50+ units - The combined tier + volume discount cannot exceed 28% - New customers (< 90 days) cannot receive tier discounts, only volume discounts

Deliverable: A function calculate_final_price(list_price, quantity, customer_tier, account_age_days, contract_price, promo_price) that returns a dict with the final price and which rules were applied.


Exercise 4.3 — Vendor Selection Engine

Build a vendor selection tool for Acme's procurement process. Given a purchase request, the tool should recommend which of three preferred vendors to use based on:

  • Vendor A: Best pricing, slowest delivery (7-10 days), can handle any quantity
  • Vendor B: Medium pricing, fast delivery (2-3 days), max order 500 units
  • Vendor C: Premium pricing, overnight delivery, handles urgent/VIP orders only, no quantity limit

Selection rules (in priority order): 1. If order is marked "urgent" and is a VIP customer order → Vendor C 2. If delivery required within 3 days → Vendor B (if quantity ≤ 500), else Vendor C 3. If quantity > 500 → Vendor A (only vendor that can handle it in standard lead time) 4. Otherwise → Vendor A (best price for standard orders)

The tool should also flag if the selection will not meet the required delivery date.

Deliverable: A Python file with select_vendor(quantity, required_days, is_urgent, is_vip_customer) and a demo run showing varied scenarios.


Tier 5: Challenge — Open-Ended Design

These exercises have no single correct answer. They ask you to think like a business systems designer.


Exercise 5.1 — Design a Returns Policy Enforcer

The scenario: A retail client wants to automate their returns processing. Their current policy is three pages long and inconsistently applied.

Your task: 1. Write a realistic returns policy (you invent the business rules — make them reasonable for a mid-size retailer) 2. Translate that policy into a Python function that takes information about a return request and outputs: Approve, Store Credit Only, Exchange Only, or Decline 3. Include at least 8 distinct business rules 4. Include test cases that deliberately probe the edge cases between rules

Key decisions to make: - What information do you need to evaluate a return? - Which rules are absolute (always apply) vs. conditional (apply only if other conditions are met)? - What happens when two rules conflict?

Write a brief (one paragraph) justification of your design choices in a comment at the top of the file.


Exercise 5.2 — The Rule Priority Problem

You are building a discount engine for a company that has accumulated discounts from multiple sources over the years:

  • Loyalty program discounts
  • Seasonal promotional discounts
  • Bulk order discounts
  • Salesperson discretionary discounts (capped at 5%)
  • Legacy "grandfather" pricing for old accounts

The problem: These discounts were originally designed independently and the company never established a clear precedence order. Now customers are asking to combine them, and the finance team is upset about margin erosion.

Your task: Design (but not necessarily fully code) a discount precedence system. Write a design document (in comments or a docstring) that addresses:

  1. What order should the discounts be applied?
  2. Which discounts should be mutually exclusive?
  3. What should the maximum combined discount be?
  4. How should the system handle "stacking" (multiple discounts applying to the same order)?

Then implement at least the framework — the function signature, the constants, and the structure of the conditional logic — even if you leave some branches as pass or TODO comments.


Exercise 5.3 — Refactor a Real Policy

Find a policy document in your real life — an expense reimbursement policy, a return policy, a service-level agreement, a pet policy for an apartment building, a library fine structure. Anything with conditional rules.

  1. Extract 6-10 distinct decision rules from the document
  2. Identify the inputs you would need
  3. Identify the possible outputs
  4. Write a Python function that encodes those rules
  5. Write 5 test cases, including at least 2 that test edge cases the original document does not address clearly

Reflection: What ambiguities did you find in the original document that forced you to make a decision when writing the code? What does this tell you about the difference between a policy written for humans and a policy written for computers?


Answer Guidance

For Tier 1 and 2 exercises, the learning goal is fluency with syntax. If your code runs and produces the correct output, it is correct.

For Tier 3 exercises, focus on being able to explain why the code behaves as it does — not just what it outputs.

For Tier 4 exercises, your system should handle all the edge cases, not just the happy path. Think about what happens with missing data, zero values, and boundary conditions.

For Tier 5 exercises, there is no answer key. Discuss your design choices with a peer or instructor and focus on being able to articulate the tradeoffs you made.