Skip to main content
Advertisement

4.1 if / elif / else — Advanced Conditional Branching

Conditional statements are the core structure that allows a program to take different paths depending on the situation. Python's if / elif / else may look simple, but used correctly, it can express complex business logic as clear, maintainable code.


Basic if / elif / else Structure

# Basic syntax
score = 85

if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
elif score >= 60:
grade = "D"
else:
grade = "F"

print(f"Score {score} → Grade: {grade}") # Score 85 → Grade: B

In Python, conditional blocks are separated by indentation. Only the first block whose condition is True executes; the rest are skipped.

# Using consecutive if (not elif) checks them all independently
x = 10

if x > 5:
print("Greater than 5") # Executes
if x > 8:
print("Greater than 8") # Executes — independent of the previous if
if x > 15:
print("Greater than 15") # Skipped

Compound Conditions: and, or, not

age = 25
has_license = True
is_sober = True

# and: all conditions must be True
if age >= 18 and has_license and is_sober:
print("Can drive")

# or: at least one must be True
is_weekend = False
is_holiday = True

if is_weekend or is_holiday:
print("Today is a day off")

# not: invert a condition
is_raining = False

if not is_raining:
print("No umbrella needed")

# Compound condition — use parentheses to clarify precedence
temperature = 30
humidity = 80

if temperature >= 28 and (humidity >= 70 or humidity <= 20):
print("High discomfort index: humid or very dry heat")

Short-circuit Evaluation

data = None
# If data is None, data.strip() is never called — prevents AttributeError
if data is not None and data.strip():
print("Valid data")
else:
print("No data")

# or: if the first is True, the second is not evaluated
default_name = ""
username = default_name or "Guest"
print(username) # Guest — empty string is falsy

Avoiding Nested if: Patterns

Deeply nested if blocks create "Arrow Code" that is hard to read.

# Bad: deep nesting
def process_order_bad(order):
if order is not None:
if order.get("user_id"):
if order.get("items"):
if len(order["items"]) > 0:
if order.get("payment_method"):
return "Order processed"
else:
return "No payment method"
else:
return "No order items"
else:
return "No order items"
else:
return "No user ID"
else:
return "No order"

Early Return Pattern

# Good: remove nesting using early return
def process_order(order):
if order is None:
return "No order"

if not order.get("user_id"):
return "No user ID"

if not order.get("items"):
return "No order items"

if len(order["items"]) == 0:
return "No order items"

if not order.get("payment_method"):
return "No payment method"

return "Order processed"


orders = [
None,
{"user_id": "u1", "items": [], "payment_method": "card"},
{"user_id": "u1", "items": ["item1"], "payment_method": None},
{"user_id": "u1", "items": ["item1"], "payment_method": "card"},
]

for order in orders:
print(process_order(order))

Guard Clause Pattern

def calculate_discount(user, cart):
# Guards: precondition checks
if not user:
raise ValueError("User information is required")
if not cart or len(cart) == 0:
return 0.0

# Core logic — only reached if guards pass
base_discount = 0.05 # 5%

if user.get("is_premium"):
base_discount += 0.10 # Premium bonus 10%

if len(cart) >= 5:
base_discount += 0.03 # 5+ items bonus 3%

return min(base_discount, 0.30) # Max 30%


print(calculate_discount({"is_premium": True}, ["a", "b", "c", "d", "e"])) # 0.18

Dictionary Dispatch Pattern (Multi-branch without match-case)

# Bad: long elif chain
def handle_command_bad(command):
if command == "start":
return "Service started"
elif command == "stop":
return "Service stopped"
elif command == "restart":
return "Service restarted"
elif command == "status":
return "Status checked"
else:
return f"Unknown command: {command}"


# Good: dictionary dispatch
COMMAND_HANDLERS = {
"start": lambda: "Service started",
"stop": lambda: "Service stopped",
"restart": lambda: "Service restarted",
"status": lambda: "Status checked",
}

def handle_command(command: str) -> str:
handler = COMMAND_HANDLERS.get(command)
if handler is None:
return f"Unknown command: {command}"
return handler()


for cmd in ["start", "status", "unknown"]:
print(handle_command(cmd))

Ternary Operator (Conditional Expression)

# Syntax: value_if_true if condition else value_if_false
age = 20
label = "Adult" if age >= 18 else "Minor"
print(label) # Adult

# Nested ternary — avoid more than 2 levels
score = 75
grade = "A" if score >= 90 else ("B" if score >= 80 else ("C" if score >= 70 else "D"))
print(grade) # C

# Useful: combined with list comprehensions
numbers = [-3, -1, 0, 2, 5, -7]
abs_values = [x if x >= 0 else -x for x in numbers]
print(abs_values) # [3, 1, 0, 2, 5, 7]

# Determining arguments in a function call
def connect(host: str, port: int, use_ssl: bool = False):
scheme = "https" if use_ssl else "http"
return f"{scheme}://{host}:{port}"

print(connect("example.com", 443, use_ssl=True)) # https://example.com:443
print(connect("localhost", 8080)) # http://localhost:8080

Real-world Example 1: Input Validation

from typing import Any


def validate_username(username: Any) -> tuple[bool, str]:
"""Validate username. Returns (is_valid, error_message)."""
if not isinstance(username, str):
return False, "Username must be a string"

username = username.strip()

if len(username) < 3:
return False, "Username must be at least 3 characters"

if len(username) > 20:
return False, "Username must be at most 20 characters"

if not username.replace("_", "").replace("-", "").isalnum():
return False, "Username may only contain letters, numbers, _, and -"

if username[0].isdigit():
return False, "Username must not start with a digit"

return True, ""


test_cases = ["ab", "valid_user", "1invalid", "user@name", "a" * 21, "good-name"]
for name in test_cases:
valid, msg = validate_username(name)
status = "OK" if valid else f"Error: {msg}"
print(f" '{name}' → {status}")

Real-world Example 2: State Machine

from dataclasses import dataclass
from typing import Literal

OrderStatus = Literal["pending", "confirmed", "shipped", "delivered", "cancelled"]


@dataclass
class Order:
order_id: str
status: OrderStatus = "pending"

def transition(self, new_status: OrderStatus) -> bool:
"""Simple state machine managing allowed transitions."""
allowed_transitions = {
"pending": {"confirmed", "cancelled"},
"confirmed": {"shipped", "cancelled"},
"shipped": {"delivered"},
"delivered": set(), # Terminal state
"cancelled": set(), # Terminal state
}

if new_status not in allowed_transitions.get(self.status, set()):
print(f" Transition not allowed: {self.status}{new_status}")
return False

print(f" Transition successful: {self.status}{new_status}")
self.status = new_status
return True


order = Order("ORD-001")
order.transition("confirmed") # Success
order.transition("delivered") # Fail: cannot go directly confirmed → delivered
order.transition("shipped") # Success
order.transition("delivered") # Success
order.transition("cancelled") # Fail: terminal state
print(f" Final status: {order.status}")

Pro Tips

1. Use Truthy / Falsy Values Actively

# Falsy values in Python
falsy_values = [None, False, 0, 0.0, 0j, "", [], {}, set(), ()]

items = []
# Bad: if len(items) == 0:
# Good:
if not items:
print("No items")

name = None
# Bad: if name is not None and name != "":
# Good:
if name:
print(f"Hello, {name}!")

2. Comparison Chaining

age = 25
# Bad: if age >= 18 and age <= 65:
# Good:
if 18 <= age <= 65:
print("Working-age range")

3. Use any() / all()

permissions = ["read", "write", "execute"]

if any(p in permissions for p in ["admin", "write"]):
print("Has write permission")

required = ["read", "write"]
if all(p in permissions for p in required):
print("All required permissions met")

4. Conditional Expression for None Handling

config_value = None
default_timeout = 30

timeout = config_value if config_value is not None else default_timeout
# Or simply (None is falsy):
timeout = config_value or default_timeout

print(timeout) # 30
Advertisement