-4

A recent coding error of mine has made me think...

I have been using assert false instead of assert False in one of my functions.

This function is invoked only inside try/except clauses.

So I never noticed this "compilation error", until I actually printed the details of the exception.

Then it made me wonder if there were any runtime differences between the two.

Of course, the "false" here can be replaced with any other undefined symbol.

Obviously, the printouts themselves would be different.

Here's a simple test that I conducted:

try:
    assert false
except Exception,e:
    print "false: class name = {:15}, message = {}".format(e.__class__.__name__,e.message)

try:
    assert False
except Exception,e:
    print "False: class name = {:15}, message = {}".format(e.__class__.__name__,e.message)

The printout of this test is:

false: class name = NameError      , message = name 'false' is not defined
False: class name = AssertionError , message = 

So my question is, are there any other runtime differences here? In particularly, I am interested to know if using assert(false) over assert(False) could somehow hinder the performance of my program.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
goodvibration
  • 5,980
  • 4
  • 28
  • 61
  • 1
    Why use **either at all**? – Martijn Pieters Aug 07 '17 at 09:03
  • 3
    Side note: always only catch *specific exceptions*. Don't play pokemon, you don't need to catch them all (or nearly all in this case). Your issue is more likely caused by you catching too many exceptions. – Martijn Pieters Aug 07 '17 at 09:04
  • @MartijnPieters: In my specific case, the function finds the position of the smallest value in a given array larger than or equal to a given input. It is doing so via binary search. At the end of the binary search, if `array[hi] >= input`, then it returns `hi`. Otherwise, if `array[lo] >= input`, then it returns `lo`. Otherwise, it `assert(False)`. – goodvibration Aug 07 '17 at 09:09
  • Just raise an exception in that case. – Martijn Pieters Aug 07 '17 at 09:10
  • @MartijnPieters: A more generic example would be an `if/elif/elif/.../else` clause. – goodvibration Aug 07 '17 at 09:10
  • @MartijnPieters: No can do. This python code emulates a code in a different language, which supports `assert` but not exceptions. I would like to keep the python emulation as close as possible to the original code (and I have an automatic script which converts it). – goodvibration Aug 07 '17 at 09:12
  • @MartijnPieters: With regards to your second comment - I am not catching any exceptions in my actual (python) program. I only stumbled upon the error that I made while investigating something else. – goodvibration Aug 07 '17 at 09:13
  • But all you are doing is raise an exception **already**. Either by using a false assertion, or by using a non-existing name. You are using round-about ways of raising an exception and are asking which one is better. **Neither is**. – Martijn Pieters Aug 07 '17 at 09:13
  • @MartijnPieters: I didn't ask that. I asked if there was any runtime difference between the two!!! – goodvibration Aug 07 '17 at 09:14
  • @goodvibration the translation reason doesn't make ```except Exception``` any less of a terrible idea: https://stackoverflow.com/questions/21553327/why-is-except-pass-a-bad-programming-practice – perigon Aug 07 '17 at 09:14
  • @perigon: This is not the topic here!!!! – goodvibration Aug 07 '17 at 09:14
  • @goodvibration: there is no need to use multiple exclamation marks. I'm giving you frank feedback on how terrible either option is. I'm not going to tell you which one to use when you should not use either one at all. That's just counter-productive. – Martijn Pieters Aug 07 '17 at 09:14
  • @goodvibration: you ask whether "assert(false) over assert(False) could somehow hinder the performance of my program." The answer is yes, either of them somehow hinders the performance of the program because they can mess you up in a lot of sneaky ways. – perigon Aug 07 '17 at 09:15
  • @MartijnPieters: Yes, I got that. You pretty much said everything besides answering the actual question... TX. – goodvibration Aug 07 '17 at 09:16
  • @perigon: That's an important piece of information, though it still goes around the question, because you are not referring to the differences between them. And BTW, it would be useful if you could actually back up the "mess you up in a lot of sneaky ways" claim (in the context of hindering performance; of course it could mess me up if the symbol "false" existed in the program, but that's a different issue). – goodvibration Aug 07 '17 at 09:18
  • 1
    @goodvibration: your question is akin to: is it better to hit my toes with a mallet or a crowbar? To which the answer is: don't do either. Hitting your toes is strongly recommended against, both will damage your toes. If your goal was to have you cry out in pain, just pretend you cried out in pain. Perhaps get acting lessons. – Martijn Pieters Aug 07 '17 at 09:37
  • @goodvibration: note that you probably are translating this from a language that uses strict typing and a compiler, making it possible for the compiler to accurately predict the movement of your foot based on your statements, and would refuse to compile the code if you ever were to hurt your toes. That's what an `assert` does in such a language: tell the compiler to disallow the *possibility* of the code ever reaching a specific point and stopping you from actually hitting your toes. Assertions in Python are a runtime check, not a compile-time check, and thus serve a different purpose. – Martijn Pieters Aug 07 '17 at 09:45

2 Answers2

2

Both versions are wrong and should never be used.

  • assert false, ...: the assert statement is never reached, because the false expression raises a NameError exception. That smells of a bug in your code, and not of a deliberate act on your behalf.

    Never use deliberate errors to raise an exception. You'd have to add a comment explaining why you did this to future maintainers of the code, but there never should be a reason to use this, because better alternatives exist.

  • assert False, ...: this is a deliberate assertion failure, and looks like an attempt at debugging and not production code. Make your assertions before code that could fail if the assertion doesn't hold instead. If you need to have an exception exit your code at that point, raise an exception.

Be explicit. Raise an exception. Even raising an AssertionError exception is better:

raise AssertionError('This should never be reached; boundary checks failed')

From the two versions you should never ever use, in Python 3 assert(False, ...) is 'faster' because at least doesn't trigger a global name search. That's because in Python 3, False is a keyword and the compiler can thus optimise it by referencing a constant. However, there is little actual difference between the two. Since a deliberately failing assertion should by design never be reached, or reached at most once, worrying about how they perform is rather a moot point.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • `assert` being a statement, I don't like the parenthesis which actually cause assertions to succeed (non-empty tuples are true). As [assertions](https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement) are a debugging feature, deliberately raising them when in nodebug mode might cause additional confusion. – Yann Vernier Aug 07 '17 at 09:40
  • @YannVernier: which is why Python throws an exception in that case: `SyntaxWarning: assertion is always true, perhaps remove parentheses?` – Martijn Pieters Aug 07 '17 at 09:42
  • @YannVernier: still, I removed the parentheses; you are correct in that it's not a good idea to treat `assert` as a function. – Martijn Pieters Aug 07 '17 at 09:43
0

It's not a compilation error, both are runtime errors, and obviously there are differences:

Evaluating false produced a NameError, meaning you referred to an undefined name. It's possible for another code path to cause the name to be defined, in which case the assertion might not fail. The trivial example would be a false = True statement above it (which obviously would be horrible in another way, with such a poor variable name). Either way it is a logic error that a code path that doesn't define the name can reach the expression.

Asserting False is a way to force an AssertionError to be thrown. It's a pretty bad technique because you've added explicit code to fail without any sort of explanation why; we can't interpret the error and have to locate it in the code to figure out why it happened. Assertions should have both logical expressions and descriptions to assist in the debugging they're for.

Either way, the way you've handled any exception loses lots of information. In particular, anything about where within the try the error occurred. A normal exception traceback (which can be accessed with traceback.print_exception and its ilk) contains this information in detail.

In effect, this style of handling both assert and try-catch is preventing you from receiving useful information on any error except compilation time errors like SyntaxError and ImportError.

The only sensible use of assert False I can think of it to test the assert statement itself. In all normal use we include the condition for failure.

Yann Vernier
  • 15,414
  • 2
  • 28
  • 26