2

Most languages have a way to force 'fail early and often' through forcing all booleans in an if to be evaluate.

Suppose I have the following if statement in Python:

if(age>100 or patient_is_brain_dead or patient_lacks_left_arm):
    do something...

The problem is I may have forgotten to set either boolean for 'patient_is_brain_dead or 'has_no_left_arm'.

Since most alive people are under 100, tests 2 and 3 happen rarely since Python, seeing the 'or' coming ahead, stops evaluating if age<100 to save time.

My code is heavily compute-bound, so the overhead of testing all three cases will not degrade performance, but would catch a lot of potential bugs that would happen potentially in one-in-500 cases.

Is there a way to force Python to evaluate them all? Don't suggest reversing the order, because any case could be the rare one(s) depending on user input.

jpp
  • 159,742
  • 34
  • 281
  • 339
eSurfsnake
  • 647
  • 6
  • 17
  • 1
    I think [unit tests](https://docs.python.org/3/library/unittest.html) may be useful here. – jpp Oct 07 '18 at 02:46
  • 1
    ...or from explicitly checking that `patient_is_brain_dead is not None and patient_lacks_left_arm is not None`, a.k.a. "forgotten to set", and raising an Exception then? – Jeronimo Oct 07 '18 at 09:04
  • I think that would work but is a rather roundabout method. I thought putting it in parentheses would do the trick, but alas not. – eSurfsnake Oct 07 '18 at 10:10
  • The brain-dead and left-arm checks usually *will* happen with the line you wrote, because `or` has to keep going if the left side is false. – user2357112 Oct 07 '18 at 17:33
  • @eSurfsnake, Is that a typo? Given your description, it seems you want the logic `if age < 100 or ... or ...`. – jpp Oct 07 '18 at 17:38
  • Yes, typo, now fixed. The point is most people alive are under 100. Few people are brain dead or lacking an arm. The problem with Python, with dynamic variable instantiation, is it is easy in complex code with lots of conditions to have 3 conditions, one of which is satisfied 99.99% of the time, yet have a code bug that fails to set other boolleans or vars that occur one-in-ten-thousand times. It's ugly, but I prefer Jeronimo's answer; converting 'or' to 'not and' forces every test. Not pretty, but does the trick. – eSurfsnake Oct 08 '18 at 03:30
  • Converting `or` to `and` doesn't prevent short-circuiting, because `and` short-circuits too. (Also, I'm not sure who Jeronimo is supposed to be.) – user2357112 Oct 09 '18 at 04:23

2 Answers2

2

In my opinion, you shouldn't want to do this in production code. If a couple of variables should be Boolean and you need to check if either of them are True, you should use short-circuiting logic.

So what are your options if, as you say, you want to make sure your code doesn't fail on edge cases? You can use unit testing to test all viable scenarios. But this may be overkill for your use case. You can just use an assert statement:

assert all(isinstance(i, bool) for i in [patient_is_brain_dead, patient_lacks_left_arm])

if age > 100 or patient_is_brain_dead or patient_lacks_left_arm:
    # do something...

The benefit of such a solution is it's possible to turn off assert statements via command line -O for production code. While, for testing purposes, you can be sure your variables are of the correct type.

jpp
  • 159,742
  • 34
  • 281
  • 339
  • I come from a C systems programming environment. With compute-intensive code, doing three checks is trivial if what comes next is multiplying two 100 x 100 matrices, or big convolutions with thousands of points. Better to weed out the user (and programmer) errors, since 99.999% of execution time is not in the redundant checks. – eSurfsnake Oct 08 '18 at 03:35
  • Yep, so assert will do this for you. I'd say it's also a Pythonic approach. – jpp Oct 08 '18 at 07:16
0

No, you will need to explicitly check for None. It is not a "roundabout" way, that is just how the language works.

If you want the conditional to fail if any of the variables are not set, you can use all() to check that they aren't None:

if(all(i is not None for i in [age, patient_is_brain_dead, patient_lacks_left_arm]) and
        (age > 100 or patient_is_brain_dead or patient_lacks_left_arm)):
    do something...
Matt
  • 2,953
  • 3
  • 27
  • 46