Skip to main content

Python Assertions

Introduction

When developing software, you often make assumptions about your code—that a variable must be non-zero, that a list contains elements, or that a dictionary has certain keys. Assertions in Python help you validate these assumptions during development and debugging by testing if a condition is true. If the condition is false, the program raises an AssertionError.

Assertions serve as a defensive programming technique that helps catch logical errors early in the development process, making your code more robust and easier to debug.

Basic Syntax

The basic syntax of an assertion in Python is:

python
assert condition[, error_message]

Where:

  • condition is the expression that you want to test
  • error_message (optional) is the message that will be displayed if the assertion fails

When Python encounters an assert statement:

  1. If the condition evaluates to True, nothing happens and the program continues execution
  2. If the condition evaluates to False, an AssertionError is raised with the optional error message

Simple Examples

Let's start with some basic examples of assertions:

Example 1: Basic Assertion

python
def calculate_average(numbers):
assert len(numbers) > 0, "Cannot calculate average of an empty list"
return sum(numbers) / len(numbers)

# This works fine
print(calculate_average([1, 2, 3, 4, 5])) # Output: 3.0

# This raises an AssertionError
try:
print(calculate_average([]))
except AssertionError as e:
print(f"Error: {e}") # Output: Error: Cannot calculate average of an empty list

Example 2: Assertions with Functions

python
def get_positive_number(value):
assert isinstance(value, (int, float)), "Value must be a number"
assert value > 0, "Value must be positive"
return value

# This works fine
print(get_positive_number(5)) # Output: 5

# These raise AssertionErrors
try:
print(get_positive_number("hello"))
except AssertionError as e:
print(f"Error: {e}") # Output: Error: Value must be a number

try:
print(get_positive_number(-10))
except AssertionError as e:
print(f"Error: {e}") # Output: Error: Value must be positive

When to Use Assertions

Assertions are best used for:

  1. Debugging aid: Checking internal assumptions during development
  2. Preconditions: Validating function inputs when you expect them to meet certain criteria
  3. Postconditions: Verifying function outputs match expected results
  4. Invariants: Checking conditions that should always be true at specific points in your code

Remember that assertions are not meant to:

  • Handle expected errors in production code
  • Validate user input
  • Check for conditions that could occur during normal operation

Difference Between Assertions and Exceptions

It's important to understand the difference between assertions and regular exceptions:

FeatureAssertionsRegular Exceptions
PurposeDebugging and developmentRuntime error handling
Can be disabledYes (with -O flag)No
Typical useInternal code checksUser-facing error handling
RecoveryNot expectedOften handled with try/except

Disabling Assertions

Python allows you to disable all assertions by running your script with the -O (optimize) flag:

bash
python -O your_script.py

When run this way, all assert statements are ignored. This is important to know because:

  1. You should never rely on assertions for functionality that must work in production
  2. Assertions should be used as a development and debugging aid, not for error handling in production code

Practical Examples

Let's look at some practical use cases for assertions in real-world Python code:

Example 1: Data Processing Function

python
def process_user_data(user_dict):
# Verify the required fields are present
assert "name" in user_dict, "User data missing 'name' field"
assert "email" in user_dict, "User data missing 'email' field"
assert isinstance(user_dict.get("age"), int), "Age must be an integer"

# Process the data
return {
"username": user_dict["name"].lower().replace(" ", "_"),
"email": user_dict["email"],
"is_adult": user_dict["age"] >= 18
}

# Valid user data
user1 = {"name": "John Doe", "email": "[email protected]", "age": 25}
print(process_user_data(user1))
# Output: {'username': 'john_doe', 'email': '[email protected]', 'is_adult': True}

# Invalid user data (missing email)
user2 = {"name": "Jane Smith", "age": 17}
try:
print(process_user_data(user2))
except AssertionError as e:
print(f"Validation error: {e}") # Output: Validation error: User data missing 'email' field

Example 2: Class Invariants

python
class Rectangle:
def __init__(self, width, height):
assert width > 0, "Width must be positive"
assert height > 0, "Height must be positive"
self.width = width
self.height = height

def set_dimensions(self, width, height):
assert width > 0, "Width must be positive"
assert height > 0, "Height must be positive"
self.width = width
self.height = height

def area(self):
# Class invariant: dimensions should always be positive
assert self.width > 0 and self.height > 0, "Invalid rectangle dimensions"
return self.width * self.height

# Create a valid rectangle
rect = Rectangle(10, 5)
print(f"Area: {rect.area()}") # Output: Area: 50

# Try to create an invalid rectangle
try:
invalid_rect = Rectangle(-5, 10)
except AssertionError as e:
print(f"Error: {e}") # Output: Error: Width must be positive

Example 3: Testing Helper Function

python
def test_sorting_function():
# Test that our sorting function works correctly
result = sort_numbers([3, 1, 4, 1, 5, 9, 2, 6])
expected = [1, 1, 2, 3, 4, 5, 6, 9]

assert result == expected, f"Sorting failed: got {result}, expected {expected}"
assert len(result) == len(expected), "Sorting changed the number of elements"

print("Sorting test passed!")

def sort_numbers(numbers):
return sorted(numbers)

# This test passes
test_sorting_function() # Output: Sorting test passed!

# Let's create a broken sorting function to see the assertion fail
def test_broken_sorting():
def broken_sort(nums):
return nums[:3] # Only returns the first 3 elements!

result = broken_sort([3, 1, 4, 1, 5, 9, 2, 6])
expected = [1, 1, 2, 3, 4, 5, 6, 9]

try:
assert result == expected, f"Sorting failed: got {result}, expected {expected}"
except AssertionError as e:
print(f"Error: {e}") # Output: Error: Sorting failed: got [3, 1, 4], expected [1, 1, 2, 3, 4, 5, 6, 9]

test_broken_sorting()

Best Practices for Using Assertions

  1. Be specific with error messages: Clear error messages help debug failed assertions
  2. Use assertions for debugging, not error handling: For user errors or expected failures, use proper exception handling
  3. Don't put code with side effects in assertions: The code may not run when assertions are disabled
  4. Keep assertions simple: Complex conditions are difficult to debug
  5. Use assertions to document assumptions: They serve as executable documentation

Incorrect Usage Example

python
# Don't do this - has side effects in the assertion
assert process_data(file) is not None, "Processing failed"

# Don't do this - handles expected errors with assertions
assert user_input.isdigit(), "Please enter a number"

Correct Usage Example

python
# Good - clear assertion with informative message
result = process_data(file)
assert result is not None, "process_data returned None for file: " + file.name

# Good - proper exception handling for expected issues
if not user_input.isdigit():
raise ValueError("Please enter a number")

Common Pitfalls

  1. Relying on assertions in production code: Remember they can be disabled
  2. Using assertions for data validation: They're meant for catching programmer errors, not bad input
  3. Writing assertions with side effects: Avoid code like assert func() where func() changes program state
  4. Making assertions too complex: This makes debugging harder

Summary

Python assertions provide a simple yet powerful way to verify assumptions in your code during development and debugging. When used correctly, they can help:

  • Catch bugs early in the development process
  • Document and validate assumptions about your code
  • Make debugging easier by failing fast when something goes wrong

Remember that assertions are development tools, not error handling mechanisms for production code. They should validate conditions that should always be true unless there's a bug in your program.

Exercises

  1. Write a function that calculates the factorial of a number using assertions to check that the input is a non-negative integer.
  2. Create a BankAccount class with deposit and withdraw methods. Use assertions to ensure that the account balance never goes negative.
  3. Write a function that divides two numbers, using assertions to check that the divisor is not zero.
  4. Create a function that processes a list of student scores (0-100) and returns the average. Use assertions to validate that all scores are within the valid range.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)