'Force' Python to evaluate all boolean expressions [duplicate] - python

This question already has answers here:
How to prevent short-circuit evaluation?
(3 answers)
Closed 4 years ago.
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.

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.

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...

Related

Python lazy multiple conditions check

I have a lot of conditions to check, but condition evaluation is heavy (e.g. condition requires database access), so I have to check them lazily.
Normally, such check could be written in if clause:
if type in FOOD_PRIZES and Prize.objects.filter(type=type).exists():
pass
If the number of conditions are increasing then if clause becomes ugly.
I can make list of condition lambdas and use all method, but it looks ugly too:
conditions = [
lambda: type in FOOD_PRIZES,
lambda: Prize.objects.filter(type=type).exists()
]
if all(condition() for condition in conditions):
pass
Is there a better way to make code shorter? Is there another ways to make conditions lazy?
Your best bet is to continue what you're doing — but put your fastest conditions to check first.
all() will short-circuit, meaning that as soon as a condition evaluates to False, it will stop processing conditions, saving you the time you would expend by running the other queries.
As for what looks best — that's up to you. But it's far better to have effective, readable code than "attractive" code! And short code isn't always better. Verbosity often makes code more readable to others.
Just be careful. For example, if subsequent conditions are dependent on the first, using all can break. For example, given x='6.5', if isinstance(x, float) and x>5.5 would work but all((isinstance(x, float), x>5.5)) would error.

Python too short if condition

I want this condition to be short if it's possible, I don't want every time or
if not wall.twitter_access_token or not wall.twitter_access_token_secret or not wall.auth_user:
raise Exception("Cannot open without verify with twitter account")
if not wall.twitter_access_token or not wall.twitter_access_token_secret or not wall.auth_user:
can be equivalently written as employing De Morgan's Law
if not(wall.twitter_access_token and wall.twitter_access_token_secret and wall.auth_user)
or even better using the built-in all
if not all([wall.twitter_access_token,
wall.twitter_access_token_secret,
wall.auth_user]):
moreover, the last expression is more readable considering the fact it is when not all are true.
nevertheless, if you would repeat the expression multiple times, employ DRY principle and refactor it to a function.
The best way to do this is by using .all() function. Your code will be like:
if not all([
wall.twitter_access_token,
wall.twitter_access_token_secret,
wall.auth_user,
]):
Note: It is a good practice to place each of your list elements into a new line. It is more readable, and you may just comment the line to skip one condition (useful while debugging).

Use of OR as branch control in FP

I undertook an interview last week in which I learnt a few things about python I didn't know about (or rather realise how they could be used), first up and the content of this question is the use of or for the purposes of branch control.
So, for example, if we run:
def f():
# do something. I'd use ... but that's actually a python object.
def g():
# something else.
f() or g()
Then if f() evaluates to some true condition then that value is returned, if not, g() is evaluated and whatever value it produces is returned, whether true or false. This gives us the ability to implement an if statement using or keywords.
We can also use and such that f() and g() will return the value of g() if f() is true and the value of f() if g() is false.
I am told that this (the use of or for branch control) is a common thing in languages such as lisp (hence the lisp tag). I'm currently following SICP learning Scheme, so I can see that (or (f x) (g x)) would return the value of (g x) assuming (f x) is #f.
I'm confused as to whether there is any advantage of this technique. It clearly achieves branch control but to me the built in keywords seem more self-explanatory.
I'm also confused as to whether or not this is "functional"? My understanding of pure functional programming is that you use constructs like this (an example from my recent erlang experiments):
makeeven(N,1) -> N+1;
makeeven(N,0) -> N;
makeeven(N) -> makeeven(N,N rem 2).
Or a better, more complicated example using template meta-programming in C++ (discovered via cpp-next.com). My thought process is that one aspect of functional programming boils down the use of piecewise defined functions in code for branch control (and if you can manage it, tail recursion).
So, my questions:
Is this "functional"? It appears that way and my interviewers said they had backgrounds in functional programming, but it didn't match what I thought was functional. I see no reason why you couldn't have a logical operator as part of a function - it seems to lend itself nicely to the concept of higher order functions. I just hadn't thought that the use of logical operators was how functional programmers achieved branch control. Right? Wrong? I can see that circuits use logic gates for branch control so I guess this is a similar (related) concept?
Is there some advantage to using this technique? Is it just language conciseness/a syntax issue, or are there implications in terms of building an interpreter to using this construct?
Are there any use cases for this technique? Or is it not used very often? Is it used at all? As a self-taught guy I'd never seen it before although that in itself isn't necessarily surprising.
I apologise for jumping over so many languages; I'm simply trying to tie together my understanding across them. Feel free to answer in any language mentioned. I also apologise if I've misunderstood any definitions or am missing something vital here, I've never formally studied computer science.
Your interviewers must have had a "functional background" way back. It used to be common to write
(or (some-condition) (some-side-effect))
but in CL and in Scheme implementation that support it, it is much better written with unless. Same goes for and vs when.
So, to be more concrete -- it's not more functional (and in fact the common use of these things was for one-sided conditionals, which are not functional to begin with); there is no advantage (which becomes very obvious in these languages when you know that things are implemented as macros anyway -- for example, most or and and implementations expand to an if); and any possible use cases should use when and unless if you have them in your implementation, otherwise it's better to define them as macros than to not use them.
Oh, and you could use a combination of them instead of a two sided if, but that would be obfuscatingly ugly.
I'm not aware of any issues with the way this code will execute, but it is confusing to read for the uninitiated. In fact, this kind of syntax is like a Python anti-pattern: you can do it, but it is in no way Pythonic.
condition and true_branch or false_branch works in all languages that have short circuting logical operators. On the other hand it's not really a good idea to use in a language where values have a boolean value.
For example
zero = (1==0) and 0 or 1 # (1==0) -> False
zero = (False and 0) or 1 # (False and X) -> X
zero = 0 or 1 # 0 is False in most languages
zero = False or 1
zero = 1
As Eli said; also, performing control flow purely with logical operators tends to be taught in introductory FP classes -- more as a mind exercise, really, not something that you necessarily want to use IRL. It's always good to be able to translate any control operator down to if.
Now, the big difference between FPs and other languages is that, in more functional languages, if is actually an expression, not a statement. An if block always has a value! The C family of languages has a macro version of this -- the test? consequent : alternative construct -- but it gets really unreadable if you nest more expressions.
Prior to Python 2.5, if you want to have a control-flow expression in Python you might have to use logical operators. In Python 2.5, though, there is an FP-like if-expression syntax, so you can do something like this:
(42 if True else 7) + 35
See PEP 308
You only mention the case where there are exactly 2 expressions to evaluate. What happens if there are 5?
;; returns first true value, evaluating only as many as needed
(or (f x) (g x) (h x) (i x) (j x))
Would you nest if-statements? I'm not sure how I'd do this in Python. It's almost like this:
any(c(x) for c in [f, g, h, i, j])
except Python's any throws away the value and just returns True. (There might be a way to do it with itertools.dropwhile, but it seems a little awkward to me. Or maybe I'm just missing the obvious way.)
(As an aside: I find that Lisp's builtins don't quite correspond to what their names are in other languages, which can be confusing. Lisp's IF is like C's ternary operator ?: or Python's conditional expressions, for example, not their if-statements. Likewise, Lisp's OR is in some ways more like (but not exactly like) Python's any(), which only takes 2 expressions. Since the normal IF returns a value already, there's no point in having a separate kind of "if" that can't be used like this, or a separate kind of "or" that only takes two values. It's already as flexible as the less common variant in other languages.)
I happen to be writing code like this right now, coincidentally, where some of the functions are "go ask some server for an answer", and I want to stop as soon as I get a positive response. I'd never use OR where I really want to say IF, but I'd rather say:
(setq did-we-pass (or (try-this x)
(try-that x)
(try-some-other-thing x)
(heck-maybe-this-will-work x))
than make a big tree of IFs. Does that qualify as "flow control" or "functional"? I guess it depends on your definitions.
It may be considered "functional" in the sense of style of programming that is/was preferred in functional language. There is nothing functional in it otherwise.
It's just syntax.
It may be sometimes more readable to use or, for example:
def foo(bar=None):
bar = bar or []
...
return bar
def baz(elems):
print "You have %s elements." % (len(elems) or "no")
You could use bar if bar else [], but it's quite elaborate.

Parentheses in Python Conditionals

I have a simple question regarding the use of parentheses in Python's conditional statements.
The following two snippets work just the same but I wonder if this is only true because of its simplicity:
>>> import os, socket
>>> if ((socket.gethostname() == "bristle") or (socket.gethostname() == "rete")):
... DEBUG = False
... else:
... DEBUG = True
...
>>> DEBUG
and now without parentheses
>>> import os, socket
>>> if socket.gethostname() == "bristle" or socket.gethostname() == "rete":
... DEBUG = False
... else:
... DEBUG = True
...
>>> DEBUG
Could anyone help shed some light on this? Are there any cases where I should definitely use them?
The other answers that Comparison takes place before Boolean are 100% correct. As an alternative (for situations like what you've demonstrated) you can also use this as a way to combine the conditions:
if socket.gethostname() in ('bristle', 'rete'):
# Something here that operates under the conditions.
That saves you the separate calls to socket.gethostname and makes it easier to add additional possible valid values as your project grows or you have to authorize additional hosts.
The parentheses just force an order of operations. If you had an additional part in your conditional, such as an and, it would be advisable to use parentheses to indicate which or that and paired with.
if (socket.gethostname() == "bristle" or socket.gethostname() == "rete") and var == condition:
...
To differentiate from
if socket.gethostname() == "bristle" or (socket.gethostname() == "rete" and var == condition):
...
The parentheses are redundant in this case. Comparison has a higher precedence than Boolean operators, so the comparisons will always be performed first regardless of the parentheses.
That said, a guideline I once saw (perhaps in Practical C Programming) said something like this:
Multiplication and division first
Addition and subtraction next
Parentheses around everything else
(Yes, IIRC they left out exponentiation!)
The idea being that the precedence rules are arcane enough that nobody should be expected to remember them all, neither the original programmer nor the maintenance programmer reading the code, so it is better to make it explicit. Essentially the parentheses serve both to communicate the intent to the compiler and as documentation for the next schmoe who has to work on it.
I believe in Python those two statements will generate the same bytecode so you're not even losing any efficiency.
I was always thinking that this is part of PEP8, but apparently it's not. However in all examples you meet in PEPs, code samples and documentation you never see redundant parentheses (there is even such an inspection in PyCharm, for example).
General recommendation is to use parentheses only if it improves readability or you actually want to change the order of expression calculation (such as (a or b) and c).
Do:
if (first_expr or second_expr) and third_expr:
if first_expr or second_expr:
Don't:
if ((first_expr or second_expr) and third_expr):
if (first_expr):
if (first_expr or (second_expr and third_expr)):
In your code sample, parentheses are completely redundant, just use if socket.gethostname() == "bristle" or socket.gethostname() == "rete": (in production code, of course, in will be much more readable, but that's rather off-topic now)
In Python and many other programming languages, parentheses are not required for every expression with multiple operators. This is because operators have a defined precedence. See the table here (Section 5.15) for information on operator precedence in Python.
You can draw an analogy to arithmetic. These expressions are equivalent:
5 * 5 + 3
(5 * 5) + 3
If you mean to add three first, then you need to use the parentheses like this:
5 * (5 + 3)
Have a look at the manual. The higher you are up in the list, the operator will be applied later. "or" is above "==" , and therefore, in this particular case the answers are the same. However, for readability, and just to be sure, I would recommend parenthesis.
I just met a similar question. My conditional statement is
if count1==0 & count2==0 & count3==0:
The first result is True, the second is False, and the third is True. Intuitively, the result for this conditional statement is true. However, it's false. But the result for the following sentence is correct;
if (count1==0) & (count2==0) & (count3==0):
I still have not figured out why this happening.

"else" considered harmful in Python?

In an answer (by S.Lott) to a question about Python's try...else statement:
Actually, even on an if-statement, the
else: can be abused in truly terrible
ways creating bugs that are very hard
to find. [...]
Think twice about else:. It is
generally a problem. Avoid it except
in an if-statement and even then
consider documenting the else-
condition to make it explicit.
Is this a widely held opinion? Is else considered harmful?
Of course you can write confusing code with it but that's true of any other language construct. Even Python's for...else seems to me a very handy thing to have (less so for try...else).
S.Lott has obviously seen some bad code out there. Haven't we all? I do not consider else harmful, though I've seen it used to write bad code. In those cases, all the surrounding code has been bad as well, so why blame poor else?
No it is not harmful, it is necessary.
There should always be a catch-all statement. All switches should have a default. All pattern matching in an ML language should have a default.
The argument that it is impossible to reason what is true after a series of if statements is a fact of life. The computer is the biggest finite state machine out there, and it is silly to enumerate every single possibility in every situation.
If you are really afraid that unknown errors go unnoticed in else statements, is it really that hard to raise an exception there?
Saying that else is considered harmful is a bit like saying that variables or classes are harmful. Heck, it's even like saying that goto is harmful. Sure, things can be misused. But at some point, you just have to trust programmers to be adults and be smart enough not to.
What it comes down to is this: if you're willing to not use something because an answer on SO or a blog post or even a famous paper by Dijkstra told you not to, you need to consider if programming is the right profession for you.
I wouldn't say it is harmful, but there are times when the else statement can get you into trouble. For instance, if you need to do some processing based on an input value and there are only two valid input values. Only checking for one could introduce a bug.
eg:
The only valid inputs are 1 and 2:
if(input == 1)
{
//do processing
...
}
else
{
//do processing
...
}
In this case, using the else would allow all values other than 1 to be processed when it should only be for values 1 and 2.
To me, the whole concept of certain popular language constructs being inherently bad is just plain wrong. Even goto has its place. I've seen very readable, maintainable code by the likes of Walter Bright and Linus Torvalds that uses it. It's much better to just teach programmers that readability counts and to use common sense than to arbitrarily declare certain constructs "harmful".
If you write:
if foo:
# ...
elif bar:
# ...
# ...
then the reader may be left wondering: what if neither foo nor bar is true? Perhaps you know, from your understanding of the code, that it must be the case that either foo or bar. I would prefer to see:
if foo:
# ...
else:
# at this point, we know that bar is true.
# ...
# ...
or:
if foo:
# ...
else:
assert bar
# ...
# ...
This makes it clear to the reader how you expect control to flow, without requiring the reader to have intimate knowledge of where foo and bar come from.
(in the original case, you could still write a comment explaining what is happening, but I think I would then wonder: "Why not just use an else: clause?")
I think the point is not that you shouldn't use else:; rather, that an else: clause can allow you to write unclear code and you should try to recognise when this happens and add a little comment to help out any readers.
Which is true about most things in programming languages, really :-)
Au contraire... In my opinion, there MUST be an else for every if. Granted, you can do stupid things, but you can abuse any construct if you try hard enough. You know the saying "a real programer can write FORTRAN in every language".
What I do lots of time is to write the else part as a comment, describing why there's nothing to be done.
Else is most useful when documenting assumptions about the code. It ensures that you have thought through both sides of an if statement.
Always using an else clause with each if statement is even a recommended practice in "Code Complete".
The rationale behind including the else statement (of try...else) in Python in the first place was to only catch the exceptions you really want to. Normally when you have a try...except block, there's some code that might raise an exception, and then there's some more code that should only run if the previous code was successful. Without an else block, you'd have to put all that code in the try block:
try:
something_that_might_raise_error()
do_this_only_if_that_was_ok()
except ValueError:
# whatever
The issue is, what if do_this_only_if_that_was_ok() raises a ValueError? It would get caught by the except statement, when you might not have wanted it to. That's the purpose of the else block:
try:
something_that_might_raise_error()
except ValueError:
# whatever
else:
do_this_only_if_that_was_ok()
I guess it's a matter of opinion to some extent, but I personally think this is a great idea, even though I use it very rarely. When I do use it, it just feels very appropriate (and besides, I think it helps clarify the code flow a bit)
Seems to me that, for any language and any flow-control statement where there is a default scenario or side-effect, that scenario needs to have the same level of consideration. The logic in if or switch or while is only as good as the condition if(x) while(x) or for(...). Therefore the statement is not harmful but the logic in their condition is.
Therefore, as developers it is our responsibility to code with the wide scope of the else in-mind. Too many developers treat it as a 'if not the above' when in-fact it can ignore all common sense because the only logic in it is the negation of the preceding logic, which is often incomplete. (an algorithm design error itself)
I don't then consider 'else' any more harmful than off-by-ones in a for() loop or bad memory management. It's all about the algorithms. If your automata is complete in its scope and possible branches, and all are concrete and understood then there is no danger. The danger is misuse of the logic behind the expressions by people not realizing the impact of wide-scope logic. Computers are stupid, they do what they are told by their operator(in theory)
I do consider the try and catch to be dangerous because it can negate handling to an unknown quantity of code. Branching above the raise may contain a bug, highlighted by the raise itself. This is can be non-obvious. It is like turning a sequential set of instructions into a tree or graph of error handling, where each component is dependent on the branches in the parent. Odd. Mind you, I love C.
There is a so called "dangling else" problem which is encountered in C family languages as follows:
if (a==4)
if (b==2)
printf("here!");
else
printf("which one");
This innocent code can be understood in two ways:
if (a==4)
if (b==2)
printf("here!");
else
printf("which one");
or
if (a==4)
if (b==2)
printf("here!");
else
printf("which one");
The problem is that the "else" is "dangling", one can confuse the owner of the else. Of course the compiler will not make this confusion, but it is valid for mortals.
Thanks to Python, we can not have a dangling else problem in Python since we have to write either
if a==4:
if b==2:
print "here!"
else:
print "which one"
or
if a==4:
if b==2:
print "here!"
else:
print "which one"
So that human eye catches it. And, nope, I do not think "else" is harmful, it is as harmful as "if".
In the example posited of being hard to reason, it can be written explicitly, but the else is still necessary.
E.g.
if a < 10:
# condition stated explicitly
elif a > 10 and b < 10:
# condition confusing but at least explicit
else:
# Exactly what is true here?
# Can be hard to reason out what condition is true
Can be written
if a < 10:
# condition stated explicitly
elif a > 10 and b < 10:
# condition confusing but at least explicit
elif a > 10 and b >=10:
# else condition
else:
# Handle edge case with error?
I think the point with respect to try...except...else is that it is an easy mistake to use it to create inconsistent state rather than fix it. It is not that it should be avoided at all costs, but it can be counter-productive.
Consider:
try:
file = open('somefile','r')
except IOError:
logger.error("File not found!")
else:
# Some file operations
file.close()
# Some code that no longer explicitly references 'file'
It would be real nice to say that the above block prevented code from trying to access a file that didn't exist, or a directory for which the user has no permissions, and to say that everything is encapsulated because it is within a try...except...else block. But in reality, a lot of code in the above form really should look like this:
try:
file = open('somefile','r')
except IOError:
logger.error("File not found!")
return False
# Some file operations
file.close()
# Some code that no longer explicitly references 'file'
You are often fooling yourself by saying that because file is no longer referenced in scope, it's okay to go on coding after the block, but in many cases something will come up where it just isn't okay. Or maybe a variable will later be created within the else block that isn't created in the except block.
This is how I would differentiate the if...else from try...except...else. In both cases, one must make the blocks parallel in most cases (variables and state set in one ought to be set in the other) but in the latter, coders often don't, likely because it's impossible or irrelevant. In such cases, it often will make a whole lot more sense to return to the caller than to try and keep working around what you think you will have in the best case scenario.

Categories