Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the usage scenarios of Python assert

Tags:

python

assert

Can someone explain WHEN to use and what are the BEST SUITABLE usage scenarios for assert?

My points are:

  • Since it just equals if not then raise
  • And in optimisation mode -O it will be ignored

So what are the usage scenarios for assert in source code (not unit test)?

From my very traditional experiences, assert should only exist in unit tests, really cannot get much point why it starts appearing in Python project code more and more.

like image 390
lnshi Avatar asked Mar 06 '23 23:03

lnshi


1 Answers

A good way to think of an assert statement in library code is as a loaded comment, it's a like a little piece of documentation about how the code works (or how you thought it worked) that will blow up and make a big noise if the "comment" is ever making a claim that turns out to be wrong.

When to use assert?

When you write an assert statement into source code (not test code), you should be very confident that it will not fire, regardless of the program input. Of course you can't be 100% certain that it will never fire, but you should be sure that if it were to fire then you have made an incorrect assumption somewhere and you'll need to revisit this section of the code.

Why add asserts into library code at all? If you should be sure they won't fire, then what's the point?

  1. Because you make mistakes and you're not perfect. Using an assertion can be a safeguard against your own mistakes. It's like when you lock the car door and then check it worked anyway by trying to lift up the door handle.
  2. It prevents code from continuing if you have made an error somewhere. A plain old comment can't do that. If the logic is relying on some incorrect assumption, a well-written assertion of that assumption can protect you from the unsafe execution of code. This allows a controlled failure mode of the code, rather than the bug manifesting in some bizarre way later on, making it harder to find the root cause.
  3. It's a form of defensive programming, which can be used to protect against changes from the future! This is a good trick. A future you, or another developer working on project, may add some code in a later commit which invalidates an assumption you had made a year ago. An assertion statement is like a signpost here that says "hey, if you changed that thing, you'll need to change something here too" and draws attention to some subtle piece of implementation detail that could be easy to miss otherwise.

When NOT to use assert

Don't use them to validate input! Use exceptions for that if necessary. If an assertion fires, that's a bug. If a user reports an unhandled AssertionError, it is your problem and not the user's fault. Something needs to be fixed.

Here's an example of a bad assertion:

def square(n):
    assert isinstance(n, int)
    ...

If this fires, that was the caller's fault. Should you need it, a TypeError is more appropriate here than unhandled AssertionError.

Here's an example of an ok assertion:

s = None
while s not in {'y', 'n'}:
    s = input("do the thing? [y/n] ").lower()
if s == 'y':
    # do the thing
else:
    assert s == 'n'
    # do other stuff 

It doesn't validate data, i.e. the user can't type any input that would cause an assertion fire - the "assumption" the developer is making here is that since the while loop has exited, s must be 'y' or 'n'. This is better than an elif s == 'n':, else: raise type construction, because that else: block could never be reached, so it would not receive test coverage unless you do some really intrusive mocking. Last but not least, it protects against entering the handling for the 'n' branch incorrectly should the foolish future-you add 6 more choices to the prompt but only add handling for 5 of those choices (oops!)

like image 189
wim Avatar answered Mar 17 '23 03:03

wim