Python’s assert statements lend itself very well to lightweight design by contract programming, like allowing you to define and check pre- and postconditions on functions or methods.

For example, let’s assume that we want to write a function that sends emails:

import mailsender


def is_email_address(email: str) -> bool:
    """
    This function checks if string 'email' represents a vaild email address
    """
    ... checking, checking ...
    

def send_mail_to(sender: str, receiver: str, text: str):
    # precondition(s)
    assert is_email_address(sender)
    assert is_email_address(receiver)
    assert text.strip()
    
    success = mailsender.send(sender, receiver, text)
    
    # postcondition(s)
    assert success, "Sending email failed!"

Here, we can use assertions to ensure that the email addresses of both sender and receiver are valid and that sending the actual e-mail succeeded1.

This approach can be very helpful during development as it allows you to essentially “state” our requirements and then start developing against them. Additionally, assertions can be used to do some sort of “type checking” in dynamically typed languages like Python.

However, it’s important to remember that using assertions impacts performance (especially if used too … generously). Luckily for us, the Python interpreter comes with an option -O that allows us to disable assertion checks:

# script 'test_assertions.py' 
def check_number(num):
    # deliberate side effect
    print(f"Checking: {num}")
    return type(num) == int and 0 <= num


def add(a, b):
    assert check_number(a)
    assert check_number(b)
    result = a + b
    assert check_number(result)
    return result


a = 123_456
b = 789_012
print(f"a={a}, b={b} --> a+b = {add(a, b)}")
$ python test_assertions.py
 Checking: 123456
 Checking: 789012
 Checking: 912468
 a=123456, b=789012 --> a+b = 912468
$ python -O test_assertions.py
 a=123456, b=789012 --> a+b = 912468

Conclusion: Python’s assert statements are meant to help developers to check requirements (as well as your own assumptions), help you debug your code and catch bugs and other “oversights” early on. They are an essential addition to other means of testing such as unit and integration tests (you know, the things that you’ll absolutely, most definitely add at some point).

Also, although I’ve only talked about assertions in Python so far, they aren’t limited to Python. In fact, many widely used programming languages have either built-in support for assertions or offer similar features through libraries or frameworks. Some languages, like Clojure take this concept even further by integrating pre- and postconditions directly into their function syntax instead of having a general “assertion” statement / expression / construct.

They are there, they don’t cost much, so go and use them :)


  1. Also, before anyone asks or points this out: mailsender as a Python library for sending emails is something that I made up during writing. I’ve no clue if it’s an actually Python library and, quite frankly, it doesn’t matter :P↩︎