The if, elif, and else Statements
The if, elif, and else Statements
After reading this chapter, you will master multi-branch decision trees using if, elif, and else, evaluate truthiness and falsy values across standard types, leverage short-circuiting for defensive execution, and apply the Bouncer pattern to eliminate deeply nested conditional logic.
Mental model
Python evaluates conditional blocks linearly from top to bottom. As soon as a branch condition evaluates to a truthy value, CPython executes that branch’s block and immediately skips all subsequent elif and else blocks:
Branching Execution Pipeline:
[ if condition ]
/ \
True False
/ \
[ if block ] [ elif condition ]
│ / \
│ True False
│ / \
│ [ elif block ] [ else block ]
│ │ │
▼ ▼ ▼
└───────────┴───────────────┘
│
▼
[ Resume Program ]
Short-Circuit Evaluation Guarding
Logical operators and and or stop evaluating operands as soon as the outcome is mathematically guaranteed:
A and B: IfAis falsy, Python returnsAimmediately without ever evaluatingB.A or B: IfAis truthy, Python returnsAimmediately without ever evaluatingB.
Defensive Guard Pipeline:
user is not None and user.is_active and check_permissions(user)
│
├─ If user is None ──────▶ Short-circuits! Never queries user.is_active
└─ If user is not None
│
└─ If not active ──▶ Short-circuits! Never calls check_permissions()
Minimal example
Save as if_decision_trees.py:
# if_decision_trees.py
def classify_http_status(status_code: int) -> str:
"""Classify HTTP response codes using a clean decision ladder."""
if 200 <= status_code < 300:
return "SUCCESS"
elif 300 <= status_code < 400:
return "REDIRECTION"
elif 400 <= status_code < 500:
return "CLIENT_ERROR"
elif 500 <= status_code < 600:
return "SERVER_ERROR"
else:
return "INVALID_STATUS"
def main() -> None:
test_codes = [200, 301, 404, 503, 999]
for code in test_codes:
category = classify_http_status(code)
print(f"HTTP {code:3d} -> Category: {category}")
if __name__ == "__main__":
main()Run via uv run python if_decision_trees.py:
HTTP 200 -> Category: SUCCESS
HTTP 301 -> Category: REDIRECTION
HTTP 404 -> Category: CLIENT_ERROR
HTTP 503 -> Category: SERVER_ERROR
HTTP 999 -> Category: INVALID_STATUS
Worked examples
Case 1: The Bouncer Pattern (Guard Clauses vs Arrow Anti-Pattern)
Deeply nested if statements create the “Arrow Anti-Pattern” (code indenting further and further to the right). The Bouncer Pattern inverts checks into early returns, keeping the happy path at indentation level 0:
# bouncer_pattern.py
from typing import Any
# THE ANTI-PATTERN (Arrow Code):
def process_request_nested(request: dict[str, Any]) -> str:
if request:
if "user" in request:
if request["user"].get("is_authenticated"):
if request["user"].get("has_permission"):
return "Action Authorized"
else:
return "Forbidden: Missing Permission"
else:
return "Unauthorized: User Not Authenticated"
else:
return "Bad Request: No User Field"
else:
return "Bad Request: Empty Payload"
# THE IDIOMATIC PATTERN (Bouncer Clauses):
def process_request_clean(request: dict[str, Any]) -> str:
# Guard 1: Empty request
if not request:
return "Bad Request: Empty Payload"
# Guard 2: Missing user
user = request.get("user")
if not user:
return "Bad Request: No User Field"
# Guard 3: Authentication
if not user.get("is_authenticated"):
return "Unauthorized: User Not Authenticated"
# Guard 4: Permission
if not user.get("has_permission"):
return "Forbidden: Missing Permission"
# Happy path at top-level indentation
return "Action Authorized"
if __name__ == "__main__":
sample_request = {
"user": {
"is_authenticated": True,
"has_permission": True,
}
}
print("Nested result:", process_request_nested(sample_request))
print("Clean result :", process_request_clean(sample_request))Run:
uv run python bouncer_pattern.pyOutput:
Nested result: Action Authorized
Clean result : Action Authorized
Case 2: Truthiness and The Numeric 0 Trap
Every Python object possesses a boolean truth value. In Python, the following objects evaluate to False: - None and False - Numeric zeros: 0, 0.0, 0j - Empty containers: "", (), [], {}, set()
Everything else evaluates to True. When validating numeric metrics, checking if not metric: incorrectly treats 0 as an error:
# falsy_metric_audit.py
def record_dropped_packets(count: int | None) -> None:
# DANGEROUS: If count is 0, 'not count' evaluates to True!
if not count:
print(f"[Naive Check] Invalid or zero metric: {count}")
# SAFE: Explicitly check for None
if count is None:
print("[Safe Check] Metric was omitted (None).")
else:
print(f"[Safe Check] Valid metric recorded: {count} dropped packets.")
if __name__ == "__main__":
record_dropped_packets(0) # Zero is a perfectly healthy metric!
record_dropped_packets(None) # Truly missing metricRun:
uv run python falsy_metric_audit.pyOutput:
[Naive Check] Invalid or zero metric: 0
[Safe Check] Valid metric recorded: 0 dropped packets.
[Naive Check] Invalid or zero metric: None
[Safe Check] Metric was omitted (None).
Pitfalls
Pitfall 1: Comparing Explicitly to True or False
# ANTI-PATTERN:
if is_ready == True:
pass
# ANTI-PATTERN:
if is_ready is True:
pass
# IDIOMATIC PYTHON:
if is_ready:
passDirect comparisons to True break custom classes implementing __bool__() and violate Pythonic conventions.
Pitfall 2: Combining and with or Without Parentheses
Python evaluates not before and, and and before or. Ambiguous unparenthesized chains cause security bypasses:
# AMBIGUOUS:
# is_admin or is_manager and is_verified
# Python parses this as: is_admin or (is_manager and is_verified)
# EXPLICIT AND SAFE: Always group multi-operator logic
is_allowed = (is_admin or is_manager) and is_verifiedExercises
- Write an
if/elif/elsefunctionclassify_temperature(temp_c: float) -> strthat categorizes temperatures into"FREEZING","COLD","MODERATE", and"HOT"using chained comparisons (0.0 <= temp_c < 15.0). - Refactor a three-level nested dictionary check into guard clauses using the Bouncer pattern.
- Demonstrate why
bool([])isFalsewhilebool([0])isTrue. - Write a conditional check that safely inspects
sys.argv[1]only iflen(sys.argv) > 1using short-circuit evaluation.
Further reading
- Python Language Reference: The
ifstatement. - Python Standard Library: Truth Value Testing.
- PEP 8: Style Guide for Python Code — Programming Recommendations for if statements.