Assertions
The assert statement is a debugging aid that tests conditions
during development. If a condition is true, nothing happens. If it's false, Python raises
AssertionError. Unlike exceptions for handling errors, assertions catch bugs -
they verify things that should NEVER be false if your code is correct!
Assert Statement Basics
The basic syntax is assert condition or assert condition, message.
When the condition is true, nothing happens and execution continues. When false, Python raises
AssertionError with your message. The key insight: assertions can be disabled
with the -O flag, so never use them for critical checks!
Click Run to execute your code
assert condition - Raises AssertionError if condition is falseassert condition, message - Same, with custom error messageInternally equivalent to:
if __debug__ and not condition: raise AssertionError(message)
Writing Helpful Assert Messages
Always include a message with your assertions. Good messages include the actual values that caused the failure, not just what was expected. This makes debugging much easier. The message is only evaluated when the assertion fails, so computing it has no performance impact during normal execution.
Click Run to execute your code
assert x > 0, f"x must be positive, got {x}" - Shows actual valueassert key in data, f"Missing {key} in {list(data.keys())}" - Shows contextassert result == expected, f"Expected {expected}, got {result}" - Shows bothBad:
assert x > 0 - No information on what went wrong!
When to Use Assertions
Use assertions for things that should NEVER be false if your code is correct - internal invariants, preconditions for internal functions, postconditions to verify results, and sanity checks in complex algorithms. Assertions document your assumptions and catch bugs during development.
Click Run to execute your code
- Internal invariants (things that MUST be true)
- Development-time sanity checks
- Documenting assumptions in code
- Catching programmer errors (bugs)
- Verifying algorithm correctness
Key principle: Assertions catch BUGS, not bad input!
Assert Pitfalls - When NOT to Use
Never use assert for input validation, security checks, or anything that must work
in production. Python's -O (optimize) flag disables all assertions! Also
watch out for the tuple syntax bug: assert(condition, message) is always
true because it's a non-empty tuple.
Click Run to execute your code
- User input validation (use
if + raise ValueError)- Security/authentication checks (use proper auth exceptions)
- File/network operations (use proper error handling)
- Anything that could fail in production
Why? Running
python -O removes ALL assertions!
Common Mistakes
1. Using assert for input validation
# WRONG - assert disabled with python -O!
def process_payment(amount):
assert amount > 0, "Amount must be positive" # DANGEROUS!
charge_card(amount)
# CORRECT - always validate with if/raise
def process_payment(amount):
if amount <= 0:
raise ValueError("Amount must be positive")
charge_card(amount)
2. The tuple bug - always true!
# WRONG - this ALWAYS passes!
x = -5
assert(x > 0, "x must be positive") # (False, "msg") is truthy tuple!
# CORRECT - no parentheses
x = -5
assert x > 0, "x must be positive" # Properly fails
# Or use explicit parentheses only around condition
assert (x > 0), "x must be positive" # Also correct
3. Side effects in assert (removed with -O)
# WRONG - pop() doesn't happen with -O!
assert items.pop() == expected_item
# CORRECT - separate operation from assertion
item = items.pop()
assert item == expected_item
# WRONG - file never closes with -O!
assert file.close() is None
# CORRECT - close normally, then verify if needed
file.close()
4. No message (hard to debug)
# WRONG - no information on failure
assert user_id in valid_users
# CORRECT - include useful context
assert user_id in valid_users, \
f"User {user_id} not found. Valid: {list(valid_users)[:5]}..."
# WRONG - message just restates condition
assert x > 0, "x must be greater than 0"
# CORRECT - show actual value
assert x > 0, f"x must be positive, got {x}"
5. Using assert for security checks
# WRONG - security bypassed with -O!
def delete_all_data(user):
assert user.is_admin, "Only admins can delete" # DANGEROUS!
database.delete_all()
# CORRECT - always check permissions
def delete_all_data(user):
if not user.is_admin:
raise PermissionError("Only admins can delete")
database.delete_all()
Exercise: Debug Helper
Task: Create a function with proper assertions for debugging.
Requirements:
- Create a
calculate_averagefunction - Add assertions for internal invariants (not input validation)
- Include helpful messages with actual values
- Add a postcondition to verify the result
Click Run to execute your code
Show Solution
def calculate_average(numbers):
"""
Calculate average of a list of numbers.
Uses assertions to catch bugs, not validate input.
"""
# Input validation (NOT assertions - these are user-facing)
if not isinstance(numbers, list):
raise TypeError(f"Expected list, got {type(numbers).__name__}")
if len(numbers) == 0:
raise ValueError("Cannot calculate average of empty list")
# Calculate sum
total = sum(numbers)
# Internal invariant: length should still be positive
n = len(numbers)
assert n > 0, f"Bug: length became {n} after sum()"
# Calculate average
average = total / n
# Postcondition: average should be within range of input values
min_val = min(numbers)
max_val = max(numbers)
assert min_val <= average <= max_val, \
f"Bug: average {average} outside range [{min_val}, {max_val}]"
return average
# Test the function
test_cases = [
[1, 2, 3, 4, 5],
[10, 20, 30],
[100],
[-5, 0, 5],
]
print("=== Calculate Average Tests ===\n")
for numbers in test_cases:
result = calculate_average(numbers)
print(f"avg({numbers}) = {result}")
# Test error cases
print("\n=== Error Cases ===")
try:
calculate_average([])
except ValueError as e:
print(f"Empty list: {e}")
try:
calculate_average("not a list")
except TypeError as e:
print(f"Wrong type: {e}")
Summary
- assert:
assert condition, message- raises AssertionError if false - Purpose: Catch bugs during development, not handle errors
- Good for: Internal invariants, postconditions, sanity checks
- NOT for: Input validation, security checks, production checks
- Messages: Always include actual values:
f"got {x}" - -O flag:
python -Odisables ALL assertions - Tuple bug:
assert(x, "msg")is always true! - Rule: If it could fail from user input, use raise not assert
What's Next?
Congratulations! You've completed the Error Handling module. You now know how to catch
exceptions with try/except, clean up with finally, raise your own
exceptions, create custom exception classes, and use assertions for debugging. Next, we'll
move on to Object-Oriented Programming and learn how to create classes and objects!
Enjoying these tutorials?