Python Nested Logic Patterns Practice Problems & Exercises
Practice: Nested Logic Patterns
← Back to lessonEasy
Analyze code snippets and report the maximum nesting depth of conditional logic. Depth 1 means a single if with no inner conditionals.
# We represent code snippets as lists of (indent_level, keyword) pairs.
# indent_level counts how many conditional blocks deep the line is.
snippet_a = [
(1, "if user.is_active:"),
(2, "if user.role == 'admin':"),
(3, "if user.has_permission('delete'):"),
(3, "print('can delete')"),
]
snippet_b = [
(1, "if not user:"),
(1, "return None"),
(1, "if not user.is_active:"),
(1, "return 'inactive'"),
(1, "return process(user)"),
]
snippet_c = [
(1, "if request.method == 'POST':"),
(2, "if request.body:"),
(3, "if validate(request.body):"),
(4, "if save(request.body):"),
(4, "return 'saved'"),
]
for name, snippet in [("snippet_a", snippet_a), ("snippet_b", snippet_b), ("snippet_c", snippet_c)]:
max_depth = max(level for level, _ in snippet)
print(f"{name} max depth: {max_depth}")Solution
snippet_a max depth: 3
snippet_b max depth: 1
snippet_c max depth: 4
Key insights:
- snippet_a (depth 3): Classic nested pattern — check user, then role, then permission. Each check is inside the previous one, creating a pyramid.
- snippet_b (depth 1): Guard clause pattern — every check is at the top level with an early return. Despite having multiple conditions, the maximum depth never exceeds 1.
- snippet_c (depth 4): Deeply nested request handling. Four levels of checking (method, body, validation, save) creates hard-to-read code that should be refactored.
Rule of thumb: If your nesting depth exceeds 2, consider flattening with guard clauses, extraction, or lookup tables. Deep nesting makes code harder to read, test, and maintain.
Expected Output
snippet_a max depth: 3
snippet_b max depth: 1
snippet_c max depth: 4Hints
Hint 1: Count indentation levels that start with `if`, `elif`, or `else`. Each new conditional block inside another adds one level of nesting.
Hint 2: Flat guard clauses (early returns) keep depth at 1 even if there are many checks.
Refactor the nested function into a flat version using guard clauses. Both versions must produce identical output.
# --- NESTED version (depth 3 — hard to read) ---
def check_access_nested(user, role, permission):
if user is not None:
if role in ("admin", "editor"):
if permission == "write":
return "full"
else:
return "denied"
else:
return "denied"
else:
return "denied"
# --- FLAT version (guard clauses — depth 1) ---
def check_access_flat(user, role, permission):
if user is None:
return "denied"
if role not in ("admin", "editor"):
return "denied"
if permission != "write":
return "denied"
return "full"
# --- Verify both produce the same results ---
test_cases = [
({"name": "Alice"}, "admin", "write"),
(None, "admin", "write"),
({"name": "Bob"}, "viewer", "write"),
({"name": "Carol"}, "editor", "read"),
]
print("--- NESTED version ---")
for user, role, perm in test_cases:
print(f"access: {check_access_nested(user, role, perm)}")
print("--- FLAT version ---")
for user, role, perm in test_cases:
print(f"access: {check_access_flat(user, role, perm)}")Solution
--- NESTED version ---
access: full
access: denied
access: denied
access: denied
--- FLAT version ---
access: full
access: denied
access: denied
access: denied
The flattening technique:
- Identify the innermost "happy path" — in the nested version,
return "full"is buried 3 levels deep. - Invert each condition and return the failure case immediately:
if user is not None:becomesif user is None: return "denied"if role in (...):becomesif role not in (...): return "denied"if permission == "write":becomesif permission != "write": return "denied"
- The happy path falls through all guards and sits at depth 0.
Why flat is better:
- Each guard clause is independently testable.
- Adding a new check means adding one line, not restructuring the entire pyramid.
- The reader can scan top-to-bottom: every early return is a rejection, and the final return is the success.
Expected Output
--- NESTED version ---
access: full
access: denied
access: denied
access: denied
--- FLAT version ---
access: full
access: denied
access: denied
access: deniedHints
Hint 1: Invert each condition and return early. `if user:` with code inside becomes `if not user: return "denied"` at the top.
Hint 2: After all guard clauses pass, the remaining code (the happy path) sits at the top indentation level — no nesting needed.
Replace the nested if/elif chain with a single dictionary lookup. Both must produce identical output.
# --- NESTED version (if/elif chain) ---
def status_message_nested(code):
if code == 200:
return "OK"
elif code == 201:
return "Created"
elif code == 301:
return "Moved Permanently"
elif code == 400:
return "Bad Request"
elif code == 401:
return "Unauthorized"
elif code == 403:
return "Forbidden"
elif code == 404:
return "Not Found"
elif code == 500:
return "Internal Server Error"
elif code == 502:
return "Bad Gateway"
elif code == 503:
return "Service Unavailable"
else:
return "Unknown Status"
# --- DICT version (zero nesting) ---
STATUS_MAP = {
200: "OK",
201: "Created",
301: "Moved Permanently",
400: "Bad Request",
401: "Unauthorized",
403: "Forbidden",
404: "Not Found",
500: "Internal Server Error",
502: "Bad Gateway",
503: "Service Unavailable",
}
def status_message_dict(code):
return STATUS_MAP.get(code, "Unknown Status")
# --- Verify both versions match ---
test_codes = [200, 301, 404, 500, 999]
print("--- NESTED version ---")
for code in test_codes:
print(f"http {code} -> {status_message_nested(code)}")
print("--- DICT version ---")
for code in test_codes:
print(f"http {code} -> {status_message_dict(code)}")Solution
--- NESTED version ---
http 200 -> OK
http 301 -> Moved Permanently
http 404 -> Not Found
http 500 -> Internal Server Error
http 999 -> Unknown Status
--- DICT version ---
http 200 -> OK
http 301 -> Moved Permanently
http 404 -> Not Found
http 500 -> Internal Server Error
http 999 -> Unknown Status
Why dictionaries beat long if/elif chains:
- O(1) lookup vs O(n) sequential checking — dictionaries use hash tables internally.
- Data-driven — the mapping is declared as pure data, separate from logic. You can load it from a config file, database, or API.
- Single line of logic —
STATUS_MAP.get(code, "Unknown Status")replaces 20+ lines of branching. - Easy to extend — adding a new status code means one new dictionary entry, not inserting into the middle of an if/elif chain.
- Testable — you can assert on the dictionary contents directly without calling the function.
When NOT to use a dict: When each branch has complex logic beyond returning a value (side effects, multiple statements, different argument handling). In those cases, consider a dict of functions instead.
Expected Output
--- NESTED version ---
http 200 -> OK
http 301 -> Moved Permanently
http 404 -> Not Found
http 500 -> Internal Server Error
http 999 -> Unknown Status
--- DICT version ---
http 200 -> OK
http 301 -> Moved Permanently
http 404 -> Not Found
http 500 -> Internal Server Error
http 999 -> Unknown StatusHints
Hint 1: A dictionary maps each status code to its message. Use `dict.get(key, default)` to handle unknown codes without any if-statements.
Hint 2: This pattern eliminates all branching — the dictionary IS the decision logic. Adding new codes means adding one key-value pair.
Medium
Refactor the deeply nested order processing function into a clean, flat structure. Both versions must produce identical output.
# --- NESTED version (depth 4) ---
def process_order_nested(customer, cart, coupon=None):
if customer is not None:
if customer.get("verified"):
if len(cart) > 0:
total = 0
for item in cart:
if item.get("in_stock", False):
total += item["price"] * item["qty"]
if coupon is not None:
if coupon.get("active"):
total *= (1 - coupon["discount"])
return {"total": round(total, 2), "status": "approved"}
else:
return {"total": 0, "status": "rejected: empty cart"}
else:
return {"total": 0, "status": "rejected: not verified"}
else:
return {"total": 0, "status": "rejected: no customer"}
# --- REFACTORED version (flat, extracted helpers) ---
def validate_order(customer, cart):
"""Return error message or None if valid."""
if customer is None:
return "no customer"
if not customer.get("verified"):
return "not verified"
if len(cart) == 0:
return "empty cart"
return None
def calculate_total(cart):
"""Sum prices for in-stock items only."""
return sum(
item["price"] * item["qty"]
for item in cart
if item.get("in_stock", False)
)
def apply_coupon(total, coupon):
"""Apply discount if coupon is active."""
if coupon and coupon.get("active"):
return total * (1 - coupon["discount"])
return total
def process_order_flat(customer, cart, coupon=None):
error = validate_order(customer, cart)
if error:
return {"total": 0, "status": f"rejected: {error}"}
total = calculate_total(cart)
total = apply_coupon(total, coupon)
return {"total": round(total, 2), "status": "approved"}
# --- Test data ---
orders = [
({"name": "Alice", "verified": True},
[{"name": "Widget", "price": 25.00, "qty": 2, "in_stock": True},
{"name": "Gadget", "price": 30.00, "qty": 1, "in_stock": True}],
None),
({"name": "Bob", "verified": False},
[{"name": "Widget", "price": 25.00, "qty": 1, "in_stock": True}],
None),
({"name": "Carol", "verified": True}, [], None),
({"name": "Dave", "verified": True},
[{"name": "Pro", "price": 150.00, "qty": 1, "in_stock": True}],
{"active": True, "discount": 0.05}),
(None, [{"name": "X", "price": 10, "qty": 1, "in_stock": True}], None),
]
print("--- NESTED version ---")
for i, (cust, cart, coupon) in enumerate(orders, 1):
result = process_order_nested(cust, cart, coupon)
print(f"Order {i}: total={result['total']}, status={result['status']}")
print("--- REFACTORED version ---")
for i, (cust, cart, coupon) in enumerate(orders, 1):
result = process_order_flat(cust, cart, coupon)
print(f"Order {i}: total={result['total']}, status={result['status']}")Solution
--- NESTED version ---
Order 1: total=80.00, status=approved
Order 2: total=0, status=rejected: not verified
Order 3: total=0, status=rejected: empty cart
Order 4: total=142.50, status=approved
Order 5: total=0, status=rejected: no customer
--- REFACTORED version ---
Order 1: total=80.00, status=approved
Order 2: total=0, status=rejected: not verified
Order 3: total=0, status=rejected: empty cart
Order 4: total=142.50, status=approved
Order 5: total=0, status=rejected: no customer
Three refactoring strategies used here:
-
Extract validation —
validate_order()returns an error message orNone. The main function checks once and returns early. This eliminates the nested "check customer, then check verified, then check cart" pyramid. -
Extract computation —
calculate_total()uses a generator expression with a conditional (if item.get("in_stock")). No nesting — the filter is built into the comprehension. -
Extract transformation —
apply_coupon()handles the coupon logic in isolation. Theif coupon and coupon.get("active")check is a single flat conditional.
The main function (process_order_flat) reads like a recipe:
- Validate the order. If invalid, reject.
- Calculate the total.
- Apply the coupon.
- Return the result.
Each step is independently testable, and adding new steps (e.g., tax calculation, shipping) means adding one function call — not nesting another level.
Expected Output
--- NESTED version ---
Order 1: total=80.00, status=approved
Order 2: total=0, status=rejected: not verified
Order 3: total=0, status=rejected: empty cart
Order 4: total=142.50, status=approved
Order 5: total=0, status=rejected: no customer
--- REFACTORED version ---
Order 1: total=80.00, status=approved
Order 2: total=0, status=rejected: not verified
Order 3: total=0, status=rejected: empty cart
Order 4: total=142.50, status=approved
Order 5: total=0, status=rejected: no customerHints
Hint 1: Extract validation into a separate function that returns an error message or None. The main function calls validate first and returns early on failure.
Hint 2: Separate the concerns: validation logic, price calculation, and discount application should each be their own function or block.
Implement a document workflow processor using a state-transition table instead of nested if/elif. The processor should track state history and handle invalid transitions gracefully.
class DocumentProcessor:
TRANSITIONS = {
("idle", "start_review"): ("reviewing", "Started review"),
("reviewing", "approve"): ("editing", "Review approved, editing"),
("reviewing", "publish"): ("published", "Review approved, publishing"),
("reviewing", "reject"): ("idle", "Review rejected, back to idle"),
("editing", "submit"): ("reviewing", "Edit complete, re-reviewing"),
("editing", "cancel"): ("idle", "Edit cancelled"),
("published", "archive"): ("archived", "Document archived"),
}
FINAL_STATES = {"published", "archived"}
def __init__(self):
self.state = "idle"
self.history = ["idle"]
def process(self, action):
if self.state in self.FINAL_STATES:
return f"[{self.state}] Final state reached"
key = (self.state, action)
if key not in self.TRANSITIONS:
return f"[{self.state}] Invalid action: {action}"
old_state = self.state
self.state, message = self.TRANSITIONS[key]
self.history.append(self.state)
return f"[{old_state} -> {self.state}] {message}"
# --- Run the pipeline ---
doc = DocumentProcessor()
actions = ["start_review", "approve", "submit", "publish", "archive"]
print("Processing document through pipeline...")
for action in actions:
result = doc.process(action)
print(result)
print(f"\nFinal state: {doc.state}")
print(f"History: {' -> '.join(doc.history)}")Solution
Processing document through pipeline...
[idle -> reviewing] Started review
[reviewing -> editing] Review approved, editing
[editing -> reviewing] Edit complete, re-reviewing
[reviewing -> published] Review approved, publishing
[published] Final state reached
Final state: published
History: idle -> reviewing -> editing -> reviewing -> published
Why a transition table beats nested conditionals:
The naive approach would look like this (do not write code this way):
if state == "idle":
if action == "start_review":
state = "reviewing"
elif action == "approve":
print("Invalid")
elif state == "reviewing":
if action == "approve":
state = "editing"
elif action == "publish":
...
That creates an N x M grid of nesting (N states times M actions). The transition table flattens this into a simple dictionary lookup:
- Adding a new state means adding entries to the dictionary — no restructuring.
- Invalid transitions are handled by a single
if key not incheck — no nested else clauses. - Final states are a set membership check — no repeated conditionals.
- History tracking happens in one place, not scattered across branches.
This is a core pattern in production systems: workflow engines, order status tracking, game state machines, and protocol handlers all use transition tables.
Expected Output
Processing document through pipeline...
[idle -> reviewing] Started review
[reviewing -> editing] Review approved, editing
[editing -> reviewing] Edit complete, re-reviewing
[reviewing -> published] Review approved, publishing
[published] Final state reached
Final state: published
History: idle -> reviewing -> editing -> reviewing -> publishedHints
Hint 1: Model transitions as a dictionary mapping (current_state, action) pairs to (next_state, message) tuples. The processor just looks up the transition.
Hint 2: The state machine eliminates nesting entirely — instead of `if state == X: if action == Y:`, you do `transitions[(state, action)]`.
Build a config validator that reports ALL errors at once instead of stopping at the first one. Use flat, independent validation checks — no nested conditionals.
REQUIRED_KEYS = ["host", "port"]
VALID_PROTOCOLS = {"http", "https", "tcp"}
def validate_config(config):
errors = []
# Check required keys first
for key in REQUIRED_KEYS:
if key not in config:
errors.append(f"missing required key: {key}")
# If required keys are missing, skip field-level validation
if errors:
return errors
# Each field validated independently — no nesting between fields
host = config.get("host", "")
if not isinstance(host, str) or not host.strip():
errors.append("host must be a non-empty string")
port = config.get("port", 0)
if not isinstance(port, int) or not (1 <= port <= 65535):
errors.append("port must be between 1 and 65535")
protocol = config.get("protocol", "http")
if protocol not in VALID_PROTOCOLS:
errors.append(f"protocol must be one of: {', '.join(sorted(VALID_PROTOCOLS))}")
timeout = config.get("timeout", 30)
if not isinstance(timeout, (int, float)) or timeout <= 0:
errors.append("timeout must be a positive number")
return errors
def print_validation(label, config):
print(f"--- {label} ---")
errors = validate_config(config)
if not errors:
print("Valid: True")
print("Errors: none")
else:
print("Valid: False")
print("Errors:")
for err in errors:
print(f" - {err}")
# --- Test cases ---
print_validation("Config 1 (valid)", {
"host": "localhost",
"port": 8080,
"protocol": "https",
"timeout": 60,
})
print_validation("Config 2 (multiple errors)", {
"host": "",
"port": 99999,
"protocol": "ftp",
"timeout": 10,
})
print_validation("Config 3 (missing keys)", {
"protocol": "http",
})Solution
--- Config 1 (valid) ---
Valid: True
Errors: none
--- Config 2 (multiple errors) ---
Valid: False
Errors:
- host must be a non-empty string
- port must be between 1 and 65535
- protocol must be one of: http, https, tcp
--- Config 3 (missing keys) ---
Valid: False
Errors:
- missing required key: host
- missing required key: port
The error-collection pattern vs. nested early-return:
A nested approach would check each field inside the previous check's success branch:
if "host" in config:
if isinstance(config["host"], str):
if config["host"].strip():
# now check port...
The error-collection pattern is superior here because:
- All errors reported at once — the user sees every problem in one pass, not one at a time.
- Independent checks — each field is validated in its own block with no dependency on previous checks (except the required-keys guard at the top).
- Easy to extend — adding a new field validation means adding one block, not nesting deeper.
- Two-phase validation — check structural requirements first (required keys), then field-level constraints. This avoids
KeyErrorexceptions without nesting.
When to use early-return vs. error-collection:
- Early-return: When the first error makes further checking impossible or meaningless (e.g., null user — nothing else to check).
- Error-collection: When multiple independent things can be wrong simultaneously (e.g., form validation, config files, API request bodies).
Expected Output
--- Config 1 (valid) ---
Valid: True
Errors: none
--- Config 2 (multiple errors) ---
Valid: False
Errors:
- host must be a non-empty string
- port must be between 1 and 65535
- protocol must be one of: http, https, tcp
--- Config 3 (missing keys) ---
Valid: False
Errors:
- missing required key: host
- missing required key: portHints
Hint 1: Instead of nesting checks inside each other (if host exists, then if host is a string, then if host is non-empty...), collect ALL errors in a list and return them at the end.
Hint 2: Define each validation rule as a separate function or inline check. Append errors to the list independently — no nesting between rules.
Extract the nested eligibility logic into named helper functions. The refactored version should read like plain English.
# --- NESTED version (depth 4) ---
def check_eligibility_nested(account):
if account is not None:
if account.get("status") == "active":
if account.get("tier") in ("premium", "enterprise"):
if account.get("loyalty_years", 0) >= 2:
return "eligible (premium + loyalty)"
elif account.get("total_spend", 0) >= 1000:
return "eligible (premium + high spend)"
else:
return "not eligible (premium but low engagement)"
else:
return "not eligible (basic tier)"
else:
return "not eligible (suspended)"
else:
return "not eligible (no account)"
# --- EXTRACTED version (named helpers, flat logic) ---
def has_active_account(account):
return account is not None and account.get("status") == "active"
def is_premium_tier(account):
return account.get("tier") in ("premium", "enterprise")
def meets_loyalty_threshold(account, min_years=2):
return account.get("loyalty_years", 0) >= min_years
def is_high_spender(account, min_spend=1000):
return account.get("total_spend", 0) >= min_spend
def check_eligibility_extracted(account):
if not has_active_account(account):
if account is None:
return "not eligible (no account)"
return "not eligible (suspended)"
if not is_premium_tier(account):
return "not eligible (basic tier)"
if meets_loyalty_threshold(account):
return "eligible (premium + loyalty)"
if is_high_spender(account):
return "eligible (premium + high spend)"
return "not eligible (premium but low engagement)"
# --- Test data ---
accounts = [
("Alice", {"status": "active", "tier": "premium", "loyalty_years": 5, "total_spend": 500}),
("Bob", {"status": "active", "tier": "basic", "loyalty_years": 3, "total_spend": 2000}),
("Carol", {"status": "active", "tier": "enterprise", "loyalty_years": 1, "total_spend": 5000}),
("Dave", {"status": "suspended", "tier": "premium", "loyalty_years": 4, "total_spend": 3000}),
("Eve", None),
]
print("--- NESTED version ---")
for name, acct in accounts:
print(f"{name}: {check_eligibility_nested(acct)}")
print("--- EXTRACTED version ---")
for name, acct in accounts:
print(f"{name}: {check_eligibility_extracted(acct)}")Solution
--- NESTED version ---
Alice: eligible (premium + loyalty)
Bob: not eligible (basic tier)
Carol: eligible (premium + high spend)
Dave: not eligible (suspended)
Eve: not eligible (no account)
--- EXTRACTED version ---
Alice: eligible (premium + loyalty)
Bob: not eligible (basic tier)
Carol: eligible (premium + high spend)
Dave: not eligible (suspended)
Eve: not eligible (no account)
The extraction technique step by step:
- Name each condition —
account.get("tier") in ("premium", "enterprise")becomesis_premium_tier(account). The name documents the intent. - Make helpers pure — each helper takes the account and returns a boolean. No side effects, no state.
- Flatten the main function with guard clauses — reject invalid states first (no account, suspended, wrong tier), then check positive conditions.
- Read it like English: "If not active, reject. If not premium, reject. If loyal, eligible. If high spender, eligible. Otherwise, not enough engagement."
Benefits:
- Testable in isolation —
is_premium_tier({"tier": "basic"})returnsFalsewithout setting up the entire account flow. - Reusable —
is_high_spendercan be used in other business logic (marketing emails, upgrade prompts). - Self-documenting — the function names eliminate the need for comments explaining what each
ifchecks. - Configurable — thresholds are parameters with defaults (
min_years=2,min_spend=1000), not magic numbers buried in conditionals.
Expected Output
--- NESTED version ---
Alice: eligible (premium + loyalty)
Bob: not eligible (basic tier)
Carol: eligible (premium + high spend)
Dave: not eligible (suspended)
Eve: not eligible (no account)
--- EXTRACTED version ---
Alice: eligible (premium + loyalty)
Bob: not eligible (basic tier)
Carol: eligible (premium + high spend)
Dave: not eligible (suspended)
Eve: not eligible (no account)Hints
Hint 1: Identify each "decision" in the nested structure and give it a name: `is_account_active()`, `is_premium_tier()`, `meets_loyalty_threshold()`. Each becomes a one-liner.
Hint 2: The main function becomes a flat sequence of named boolean checks — readable as English: "if account active and (premium with loyalty or high spender)..."
Hard
Build a rule engine where individual rules are composable functions. Rules can be combined with all_of (AND) and any_of (OR) combinators — no nested conditionals anywhere.
def make_rule(field, op, value, description=None):
"""Factory that creates a rule function for a single field check."""
desc = description or f"{field} {op} {value}"
def rule(data):
actual = data.get(field)
if op == ">=":
passed = actual >= value
elif op == "<=":
passed = actual <= value
elif op == "==":
passed = actual == value
elif op == "!=":
passed = actual != value
elif op == "in":
passed = actual in value
elif op == "is":
passed = actual is value
else:
passed = False
return (True, None) if passed else (False, f"Failed: {desc}")
rule.__doc__ = desc
return rule
def all_of(*rules):
"""AND combinator — all rules must pass. Returns first failure."""
def combined(data):
for rule in rules:
passed, reason = rule(data)
if not passed:
return (False, reason)
return (True, None)
return combined
def any_of(*rules):
"""OR combinator — at least one rule must pass."""
def combined(data):
reasons = []
for rule in rules:
passed, reason = rule(data)
if passed:
return (True, None)
reasons.append(reason)
return (False, " AND ".join(reasons))
return combined
# --- Define rule sets by composing atomic rules ---
credit_approval = all_of(
make_rule("age", ">=", 21, "age >= 21"),
make_rule("score", ">=", 650, "score >= 650"),
make_rule("employed", "==", True, "employed is True"),
)
premium_offer = all_of(
make_rule("age", ">=", 21, "age >= 21"),
make_rule("score", ">=", 700, "score >= 700"),
make_rule("income", ">=", 70000, "income >= 70000"),
make_rule("employed", "==", True, "employed is True"),
)
# --- Test applicants ---
applicants = [
{"name": "Alice", "age": 28, "score": 720, "income": 85000, "employed": True},
{"name": "Bob", "age": 19, "score": 580, "income": 30000, "employed": True},
{"name": "Carol", "age": 35, "score": 750, "income": 120000, "employed": False},
{"name": "Dave", "age": 45, "score": 690, "income": 55000, "employed": True},
]
rule_sets = {"credit_approval": credit_approval, "premium_offer": premium_offer}
print("--- Evaluating applicants ---")
for person in applicants:
name = person["name"]
print(f"{name} (age={person['age']}, score={person['score']}, "
f"income={person['income']}, employed={person['employed']})")
for rule_name, rule_fn in rule_sets.items():
passed, reason = rule_fn(person)
if passed:
print(f" {rule_name}: PASS")
else:
print(f" {rule_name}: FAIL -> {reason}")
print()Solution
--- Evaluating applicants ---
Alice (age=28, score=720, income=85000, employed=True)
credit_approval: PASS
premium_offer: PASS
Bob (age=19, score=580, income=30000, employed=True)
credit_approval: FAIL -> Failed: score >= 650
premium_offer: FAIL -> Failed: score >= 700
Carol (age=35, score=750, income=120000, employed=False)
credit_approval: FAIL -> Failed: employed is True
premium_offer: FAIL -> Failed: employed is True
Dave (age=45, score=690, income=55000, employed=True)
credit_approval: PASS
premium_offer: FAIL -> Failed: income >= 70000
Architecture of the rule engine:
-
Atomic rules —
make_rule("score", ">=", 650)creates a function that checks one field against one value. Each rule returns(True, None)on success or(False, reason)on failure. -
Combinators —
all_of()(AND) andany_of()(OR) take multiple rules and return a new rule with the same signature. This is function composition. -
Rule sets —
credit_approval = all_of(rule1, rule2, rule3)defines a complete policy as a single callable. No nesting anywhere.
Why this eliminates nesting entirely:
The naive nested approach would be:
if age >= 21:
if score >= 650:
if employed:
return "approved"
The rule engine replaces this with a flat list of checks. Adding a new rule is one line. Removing a rule is deleting one line. Changing AND to OR is swapping all_of for any_of.
Production uses of this pattern:
- Authorization engines — combine permission rules dynamically
- Form validation — compose field validators for different forms
- Feature flags —
any_of(is_beta_user, is_internal, has_flag("new_ui")) - Business rules — insurance underwriting, loan approval, pricing tiers
Expected Output
--- Evaluating applicants ---
Alice (age=28, score=720, income=85000, employed=True)
credit_approval: PASS
premium_offer: PASS
Bob (age=19, score=580, income=30000, employed=True)
credit_approval: FAIL -> Failed: score >= 650
premium_offer: FAIL -> Failed: score >= 700
Carol (age=35, score=750, income=120000, employed=False)
credit_approval: FAIL -> Failed: employed is True
premium_offer: FAIL -> Failed: employed is True
Dave (age=45, score=690, income=55000, employed=True)
credit_approval: PASS
premium_offer: FAIL -> Failed: income >= 70000Hints
Hint 1: Define atomic rule functions that each check ONE thing and return (True, None) or (False, reason). Then compose them with `all_of()` which runs every rule and returns the first failure.
Hint 2: The key insight: instead of nesting `if age >= 21: if score >= 650: if income >= ...`, you build a list of rules and iterate. Adding/removing rules never changes the structure.
Build a decision matrix that replaces deeply nested conditional logic for shipping method selection. The matrix maps combinations of (region, speed, size) to (method, cost).
class DecisionMatrix:
"""Data-driven decision engine — no nested conditionals."""
WILDCARD = "*"
def __init__(self, dimensions, rules, default_result):
self.dimensions = dimensions
self.rules = rules # List of (conditions_dict, result_dict)
self.default_result = default_result
def _matches(self, conditions, inputs):
"""Check if all conditions match the inputs."""
for dim in self.dimensions:
expected = conditions.get(dim, self.WILDCARD)
if expected == self.WILDCARD:
continue
if isinstance(expected, (list, tuple, set)):
if inputs.get(dim) not in expected:
return False
elif inputs.get(dim) != expected:
return False
return True
def evaluate(self, **inputs):
"""Find the first matching rule and return its result."""
for conditions, result in self.rules:
if self._matches(conditions, inputs):
return result
return self.default_result
# --- Define the shipping decision matrix ---
shipping_matrix = DecisionMatrix(
dimensions=["region", "speed", "size"],
rules=[
# Domestic rules
({"region": "domestic", "speed": "standard", "size": ("small", "medium")},
{"method": "ground", "cost": 5.99}),
({"region": "domestic", "speed": "express", "size": ("small", "medium")},
{"method": "air_priority", "cost": 15.99}),
({"region": "domestic", "speed": "overnight", "size": ("small", "medium")},
{"method": "next_day_air", "cost": 25.99}),
({"region": "domestic", "speed": "*", "size": "large"},
{"method": "freight_truck", "cost": 55.00}),
({"region": "domestic", "speed": "*", "size": "oversized"},
{"method": "freight_truck", "cost": 75.00}),
# International rules
({"region": "international", "speed": "standard", "size": "*"},
{"method": "ocean_freight", "cost": 45.00}),
({"region": "international", "speed": "express", "size": ("small", "medium")},
{"method": "air_international", "cost": 29.99}),
({"region": "international", "speed": "express", "size": ("large", "oversized")},
{"method": "cargo_air", "cost": 65.00}),
({"region": "international", "speed": "overnight", "size": ("small", "medium")},
{"method": "premium_international", "cost": 59.99}),
({"region": "international", "speed": "overnight", "size": ("large", "oversized")},
{"method": "charter_freight", "cost": 89.99}),
],
default_result={"method": "default", "cost": 9.99},
)
# --- Test orders ---
orders = [
{"region": "domestic", "speed": "standard", "size": "small"},
{"region": "domestic", "speed": "express", "size": "medium"},
{"region": "international", "speed": "standard", "size": "large"},
{"region": "international", "speed": "express", "size": "small"},
{"region": "domestic", "speed": "overnight", "size": "medium"},
{"region": "international", "speed": "overnight", "size": "large"},
{"region": "domestic", "speed": "standard", "size": "oversized"},
{"region": "unknown", "speed": "express", "size": "small"},
]
print("--- Shipping decisions ---")
for order in orders:
result = shipping_matrix.evaluate(**order)
dims = "/".join(order[d] for d in ["region", "speed", "size"])
print(f"Order: {dims} -> {result['method']}, cost=\${result['cost']}")Solution
--- Shipping decisions ---
Order: domestic/standard/small -> ground, cost=$5.99
Order: domestic/express/medium -> air_priority, cost=$15.99
Order: international/standard/large -> ocean_freight, cost=$45.00
Order: international/express/small -> air_international, cost=$29.99
Order: domestic/overnight/medium -> next_day_air, cost=$25.99
Order: international/overnight/large -> charter_freight, cost=$89.99
Order: domestic/standard/oversized -> freight_truck, cost=$75.00
Order: unknown/express/small -> default, cost=$9.99
How the decision matrix eliminates nesting:
The naive approach for 3 dimensions with 3-5 values each would create a deeply nested structure:
if region == "domestic":
if speed == "standard":
if size == "small":
...
elif size == "medium":
...
elif size == "large":
...
elif speed == "express":
if size == "small":
...
That is 3+ levels deep and grows multiplicatively. The decision matrix flattens this into a linear list of rules.
Key features of the matrix:
- Wildcards (
*) — a rule can match any value for a dimension, avoiding duplicate rules. - Collection matching —
("small", "medium")matches either value, reducing the number of rules. - First-match wins — rules are evaluated in order, so more specific rules go before general ones.
- Default fallback — if no rule matches, the default result is returned. No
elsechains needed. - Data-driven — the entire decision table can be loaded from a JSON file, database, or admin UI. No code changes needed to add shipping methods.
Real-world applications: pricing engines, tax calculation, insurance rate tables, feature flag targeting, A/B test assignment.
Expected Output
--- Shipping decisions ---
Order: domestic/standard/small -> ground, cost=$5.99
Order: domestic/express/medium -> air_priority, cost=$15.99
Order: international/standard/large -> ocean_freight, cost=$45.00
Order: international/express/small -> air_international, cost=$29.99
Order: domestic/overnight/medium -> next_day_air, cost=$25.99
Order: international/overnight/large -> charter_freight, cost=$89.99
Order: domestic/standard/oversized -> freight_truck, cost=$75.00
Order: unknown/express/small -> default, cost=$9.99Hints
Hint 1: Build a list of (conditions_dict, result) pairs. To find a match, iterate through the matrix and check if ALL conditions in a row match the input. Use a default/fallback row at the end.
Hint 2: The conditions dict can use special values like "*" for wildcard matching (match any value for that dimension).
Build a command dispatcher that replaces nested switch/if-elif structures. Each command is registered as a handler function, and the dispatcher routes by name lookup.
class FileSystem:
"""In-memory filesystem with command-pattern dispatch."""
def __init__(self):
self.files = {}
self._commands = {
"create": self._cmd_create,
"read": self._cmd_read,
"update": self._cmd_update,
"delete": self._cmd_delete,
"copy": self._cmd_copy,
"rename": self._cmd_rename,
"list": self._cmd_list,
}
def dispatch(self, command_str):
"""Parse and dispatch a command string. Zero nesting."""
parts = []
current = ""
in_quotes = False
for ch in command_str:
if ch == '"' and not in_quotes:
in_quotes = True
elif ch == '"' and in_quotes:
in_quotes = False
elif ch == ' ' and not in_quotes:
if current:
parts.append(current)
current = ""
else:
current += ch
if current:
parts.append(current)
cmd_name = parts[0] if parts else ""
args = parts[1:]
handler = self._commands.get(cmd_name)
if handler is None:
return (False, f"Unknown command: {cmd_name}")
return handler(args)
def _cmd_create(self, args):
if len(args) < 1:
return (False, "Usage: create <filename> [content]")
name = args[0]
content = args[1] if len(args) > 1 else ""
if name in self.files:
return (False, f"File already exists: {name}")
self.files[name] = content
return (True, f"Created {name} (content: '{content}', size: {len(content)})")
def _cmd_read(self, args):
if len(args) < 1:
return (False, "Usage: read <filename>")
name = args[0]
if name not in self.files:
return (False, f"File not found: {name}")
return (True, f"Content of {name} -> '{self.files[name]}'")
def _cmd_update(self, args):
if len(args) < 2:
return (False, "Usage: update <filename> <content>")
name, content = args[0], args[1]
if name not in self.files:
return (False, f"File not found: {name}")
self.files[name] = content
return (True, f"Updated {name} (new content: '{content}', size: {len(content)})")
def _cmd_delete(self, args):
if len(args) < 1:
return (False, "Usage: delete <filename>")
name = args[0]
if name not in self.files:
return (False, f"File not found: {name}")
del self.files[name]
return (True, f"Deleted {name}")
def _cmd_copy(self, args):
if len(args) < 2:
return (False, "Usage: copy <source> <dest>")
src, dest = args[0], args[1]
if src not in self.files:
return (False, f"File not found: {src}")
if dest in self.files:
return (False, f"Destination already exists: {dest}")
self.files[dest] = self.files[src]
return (True, f"Copied {src} -> {dest}")
def _cmd_rename(self, args):
if len(args) < 2:
return (False, "Usage: rename <old> <new>")
old, new = args[0], args[1]
if old not in self.files:
return (False, f"File not found: {old}")
if new in self.files:
return (False, f"Destination already exists: {new}")
self.files[new] = self.files.pop(old)
return (True, f"Renamed {old} -> {new}")
def _cmd_list(self, args):
if not self.files:
return (True, "No files")
names = ", ".join(sorted(self.files.keys()))
return (True, f"Files: {names}")
# --- Run a sequence of commands ---
fs = FileSystem()
commands = [
'create report.txt "Q4 Results"',
'read report.txt',
'update report.txt "Q4 Results - Final"',
'copy report.txt backup.txt',
'list',
'delete report.txt',
'read report.txt',
'rename backup.txt final.txt',
'unknown_cmd foo',
]
print("--- Running file operations ---")
for cmd in commands:
success, message = fs.dispatch(cmd)
status = "OK" if success else "ERROR"
print(f"> {cmd}")
print(f" {status}: {message}")
print()Solution
--- Running file operations ---
> create report.txt "Q4 Results"
OK: Created report.txt (content: 'Q4 Results', size: 10)
> read report.txt
OK: Content of report.txt -> 'Q4 Results'
> update report.txt "Q4 Results - Final"
OK: Updated report.txt (new content: 'Q4 Results - Final', size: 18)
> copy report.txt backup.txt
OK: Copied report.txt -> backup.txt
> list
OK: Files: backup.txt, report.txt
> delete report.txt
OK: Deleted report.txt
> read report.txt
ERROR: File not found: report.txt
> rename backup.txt final.txt
OK: Renamed backup.txt -> final.txt
> unknown_cmd foo
ERROR: Unknown command: unknown_cmd
The Command Pattern eliminates nested switch statements entirely:
A naive approach would nest two levels — command type, then argument validation:
if cmd == "create":
if len(args) >= 1:
if name not in files:
...
elif cmd == "read":
if len(args) >= 1:
if name in files:
...
elif cmd == "update":
...
The command pattern replaces this with:
- Registration — each command maps to a handler function in
self._commands. - Dispatch —
self._commands.get(cmd_name)does the routing in O(1). No if/elif chain. - Uniform interface — every handler takes
argsand returns(success, message). The dispatcher does not care what the command does internally. - Self-contained validation — each handler validates its own arguments independently. No shared nesting.
Adding a new command takes exactly 3 steps:
- Write the handler method (
_cmd_whatever). - Add one entry to
self._commands. - Done. No other code changes.
Production uses:
- CLI tools —
argparsesubcommands use this exact pattern internally. - Chat bots — map
/slashcommands to handler functions. - API routers — Flask and FastAPI map URL patterns to handler functions.
- Game engines — player actions dispatched to handler functions by name.
- Undo/redo systems — each command is an object with
execute()andundo()methods.
Expected Output
--- Running file operations ---
> create report.txt "Q4 Results"
OK: Created report.txt (content: 'Q4 Results', size: 10)
> read report.txt
OK: Content of report.txt -> 'Q4 Results'
> update report.txt "Q4 Results - Final"
OK: Updated report.txt (new content: 'Q4 Results - Final', size: 18)
> copy report.txt backup.txt
OK: Copied report.txt -> backup.txt
> list
OK: Files: backup.txt, report.txt
> delete report.txt
OK: Deleted report.txt
> read report.txt
ERROR: File not found: report.txt
> rename backup.txt final.txt
OK: Renamed backup.txt -> final.txt
> unknown_cmd foo
ERROR: Unknown command: unknown_cmdHints
Hint 1: Register each command as a method and map command names to handler functions in a dictionary. The dispatcher looks up the command name and calls the handler with the arguments.
Hint 2: Each handler has the same signature: takes a list of args and returns (success, message). This uniform interface means the dispatcher has zero conditionals about which command is running.
