Python assert vs raise: Key Differences
Learn the difference between Python's assert and raise statements, when each one fits, and why assert checks are disabled by optimization flags.
When writing validation checks in Python, you often need to interrupt the normal flow of execution when something is wrong. The assert statement and the raise statement are two built-in ways to do that. Both stop execution, but they serve different purposes and behave differently in production.
The core difference
assert condition, message checks a condition and raises AssertionError if the condition is false. It is a debugging aid intended for internal invariants: conditions that should always hold if the program is correct. raise is the general mechanism for throwing any exception, including custom exception classes, when the program needs to signal an error to its caller.
# Using assert def divide(a, b): assert b != 0, 'denominator must not be zero' return a / b # Using raise def divide(a, b): if b == 0: raise ValueError('denominator must not be zero') return a / b
Both prevent division by zero, but they communicate different intent. The assert version communicates that the condition is always true in a correct program; if it is false, the program is broken. The raise version communicates that this is an expected error the caller may need to handle.
How assert behaves in production
A key practical difference is that assert statements are removed when Python runs with optimization enabled, such as python -O or when PYTHONOPTIMIZE is set. In that mode, the assertion is not executed and the condition expression is not evaluated.
That means assert is not a reliable guard for validation that must always run. For example, if you use assert to check user input, an optimized run can silently skip the check. Use assert for invariants that help you find bugs during development, not for checks that protect users or data.
This also affects tests. Many test suites use assert-style checks, but if you run Python with -O, those checks disappear. If a test must run under all execution modes, use an explicit check or a test framework's assertion methods.
When to use raise
Use raise for conditions that are expected at runtime and should be handled by the caller:
- Invalid user input or API arguments
- Missing resources or failed I/O operations
- Business rule violations
- Any situation where the caller should have a chance to recover with
try/except
def set_quantity(quantity): if quantity < 0: raise ValueError('quantity must not be negative') # ...
You can also define custom exception classes:
class InsufficientFundsError(Exception): pass def withdraw(balance, amount): if amount > balance: raise InsufficientFundsError('amount exceeds available balance') return balance - amount
When to use assert
Use assert to catch programmer errors and document internal assumptions:
- Invariants at the start or end of a function
- Conditions that should never be false unless there is a bug
- Checks that are safe to remove in optimized runs
def apply_discount(price, discount): assert 0 <= discount <= 1, 'discount must be a fraction between 0 and 1' return price * (1 - discount)
If a check is essential to correctness, safety, or security, prefer an explicit raise. If it is a sanity check that should catch bugs during development, assert can be appropriate.
Summary
Use raise for error conditions the caller may need to handle. Use assert for programmer-facing invariants that are only needed when assertions are enabled. The two mechanisms look similar, but they have different reliability in production: raise remains active in optimized runs, while assert can be removed by optimization flags.