Python lazy multiple conditions check - python

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.

Related

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

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

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

Python - disposable ifs

While writing state machines to analyze different types of text data, independent of language used (VBA to process .xls contents using arrays/dictionaries or PHP/Python to make SQL insert queries out of .csv's) I often ran into neccesity of something like
boolean = False
while %sample statement%:
x = 'many different things'
if boolean == False:
boolean = True
else:
%action that DOES depend on contents of x
that need to do every BUT first time I get to it%
Every time I have to use a construction like this, I can't help feeling noob. Dear algorithmic gurus, can you assure me that it's the only way out and there is nothing more elegant? Any way to specify that some statement should be "burnt after reading"? So that some stupid boolean is not going to be checked each iteration of the loop
The only things that come across as slightly "noob" about this style are:
Comparing a boolean variable to True or False. Just write if <var> or if not <var>. (I'll ignore the = vs == as a typo!)
Not giving the boolean variable a good name. I know that here boolean is just a placeholder name, but in general using a name like first_item_seen rather than something generic can make the code a lot more readable:
first_item_seen = False
while [...]:
[...]
if first_item_seen:
[...]
else:
first_item_seen = True
Another suggestion that can work in some circumstances is to base the decision on another variable that naturally conveys the same state. For instance, it's relatively common to have a variable that contains None for the first iteration, but contains a value for later iterations (e.g. the result so far); using this can make the code slightly more efficient and often slightly clearer.
If I understand your problem correctly, I'd try something like
x = 'many different things'
while %sample statements%:
x = 'many different things'
action_that_depends_on_x()
It is almost equivalent; the only difference is that in your version the loop body could be never executed (hence x never being computed, hence no side effects of computing x), in my version it is always computed at least once.

Embedded if statements

Suppose I have a function like the following:
bigrams=[(k,v) for (k,v) in dict_bigrams.items()
if k[:pos_qu]==selection[:pos_qu]
and (k[pos_qu+1:]==selection[pos_qu+1:] if pos_qu!=1)
and k[pos_qu] not in alphabet.values()]
I want to make the second condition, namely k[pos_qu+1:]==selection[pos_qu+1:] dependent from another if statement, if pos_qu!=1. I tried (as shown above) by including the two together into parentheses but python flags a syntax error at the parentheses
If I understand your requirement correctly, you only want to check k[pos_qu+1:]==selection[pos_qu+1:] if the condition pos_qu!=1 is also met. You can rephrase that as the following condition:
pos_qu==1 or k[pos_qu+1:]==selection[pos_qu+1:]
Putting this into your comprehension:
bigrams=[(k,v) for (k,v) in dict_bigrams.items()
if k[:pos_qu]==selection[:pos_qu]
and (pos_qu==1 or k[pos_qu+1:]==selection[pos_qu+1:])
and k[pos_qu] not in alphabet.values()]
Whenever you find yourself with a complex list comprehension, trying to figure out how to do something complicated and not knowing how, the answer is usually to break things up. Expression syntax is inherently more limited than full statement (or multi-statement suite) syntax in Python, to prevent you from writing things that you won't be able to read later. Usually, that's a good thing—and, even when it isn't, you're better off going along with it than trying to fight it.
In this case, you've got a trivial comprehension, except for the if clause, which you don't know how to write as an expression. So, I'd turn the condition into a separate function:
def isMyKindOfKey(k):
… condition here
[(k,v) for (k,v) in dict_bigrams.items() if isMyKindOfKey(k)]
This lets you use full multi-statement syntax for the condition. It also lets you give the condition a name (hopefully something better than isMyKindOfKey); makes the parameters, local values captured by the closure, etc. more explicit; lets you test the function separately or reuse it; etc.
In cases where the loop itself is the non-trivial part (or there's just lots of nesting), it usually makes more sense to break up the entire comprehension into an explicit for loop and append, but I don't think that's necessary here.
It's worth noting that in this case—as in general—this doesn't magically solve your problem, it just gives you more flexibility in doing so. For example, you can use the same transformation from postfix if to infix or that F.J suggests, but you can also leave it as an if, e.g., like this:
def isMyKindOfKey(k):
retval = k[:pos_qu]==selection[:pos_qu]
if pos_qu!=1:
retval = retval and (k[pos_qu+1:]==selection[pos_qu+1:])
retval = retval and (k[pos_qu] not in alphabet.values())
return retval
That probably isn't actually the way I'd write this, but you can see how this is a trivial way to transform what's in your head into code, which would be very hard to do in an expression.
just change the order
bigrams=[(k,v) for (k,v) in dict_bigrams.items()
if k[:pos_qu]==selection[:pos_qu] #evaluated first
and pos_qu!=1 #if true continue and evaluate this next
and (k[pos_qu+1:]==selection[pos_qu+1:]) #if pos_qu != 1 lastly eval this
as the comment mentions this is not a very pythonic list comprehension and would be much more readable as a standard for loop..

When to use "while" or "for" in Python

When I should use a while loop or a for loop in Python? It looks like people prefer using a for loop (for brevity?). Is there any specific situation which I should use one or the other? Is it a matter of personal preference? The code I have read so far made me think there are big differences between them.
Yes, there is a huge difference between while and for.
The for statement iterates through a collection or iterable object or generator function.
The while statement simply loops until a condition is False.
It isn't preference. It's a question of what your data structures are.
Often, we represent the values we want to process as a range (an actual list), or xrange (which generates the values) (Edit: In Python 3, range is now a generator and behaves like the old xrange function. xrange has been removed from Python 3). This gives us a data structure tailor-made for the for statement.
Generally, however, we have a ready-made collection: a set, tuple, list, map or even a string is already an iterable collection, so we simply use a for loop.
In a few cases, we might want some functional-programming processing done for us, in which case we can apply that transformation as part of iteration. The sorted and enumerate functions apply a transformation on an iterable that fits naturally with the for statement.
If you don't have a tidy data structure to iterate through, or you don't have a generator function that drives your processing, you must use while.
while is useful in scenarios where the break condition doesn't logically depend on any kind of sequence. For example, consider unpredictable interactions:
while user_is_sleeping():
wait()
Of course, you could write an appropriate iterator to encapsulate that action and make it accessible via for – but how would that serve readability?¹
In all other cases in Python, use for (or an appropriate higher-order function which encapsulate the loop).
¹ assuming the user_is_sleeping function returns False when false, the example code could be rewritten as the following for loop:
for _ in iter(user_is_sleeping, False):
wait()
The for is the more pythonic choice for iterating a list since it is simpler and easier to read.
For example this:
for i in range(11):
print i
is much simpler and easier to read than this:
i = 0
while i <= 10:
print i
i = i + 1
for loops is used
when you have definite itteration (the number of iterations is known).
Example of use:
Iterate through a loop with definite range: for i in range(23):.
Iterate through collections(string, list, set, tuple, dictionary): for book in books:.
while loop is an indefinite itteration that is used when a loop repeats
unkown number of times and end when some condition is met.
Note that in case of while loop the indented body of the loop should modify at least one variable in the test condition else the result is infinite loop.
Example of use:
The execution of the block of code require that the user enter specified input: while input == specified_input:.
When you have a condition with comparison operators: while count < limit and stop != False:.
Refrerences: For Loops Vs. While Loops, Udacity Data Science, Python.org.
First of all there are differences between the for loop in python and in other languages.
While in python it iterates over a list of values (eg: for value in [4,3,2,7]), in most other languages (C/C++, Java, PHP etc) it acts as a while loop, but easier to read.
For loops are generally used when the number of iterations is known (the length of an array for example), and while loops are used when you don't know how long it will take (for example the bubble sort algorithm which loops as long as the values aren't sorted)
Consider processing iterables. You can do it with a for loop:
for i in mylist:
print i
Or, you can do it with a while loop:
it = mylist.__iter__()
while True:
try:
print it.next()
except StopIteration:
break
Both of those blocks of code do fundamentally the same thing in fundamentally the same way. But the for loop hides the creation of the iterator and the handling of the StopIteration exception so that you don't need to deal with them yourself.
The only time I can think of that you'd use a while loop to handle an iterable would be if you needed to access the iterator directly for some reason, e.g. you needed to skip over items in the list under some circumstances.
For loops usually make it clearer what the iteration is doing. You can't always use them directly, but most of the times the iteration logic with the while loop can be wrapped inside a generator func. For example:
def path_to_root(node):
while node is not None:
yield node
node = node.parent
for parent in path_to_root(node):
...
Instead of
parent = node
while parent is not None:
...
parent = parent.parent
while loop is better for normal loops
for loop is much better than while loop while working with strings, like lists, strings etc.
If your data is dirty and it won't work with a for loop, you need to clean your data.
For me if your problem demands multiple pointers to be used to keep
track of some boundary I would always prefer While loop.
In other cases it's simply for loop.

Categories