python conditional statement unreasonble results - python

I try to understand why I get unreasonable result from the following if:
def print_if_neg (a,b):
if a < 0 != b < 0:
print "Only One Neg"
else:
print "0 or 2"
print_if_neg(1,1)
print_if_neg(-1,1)
print_if_neg (1,-1)
print_if_neg(-1,-1)
I get 3 times 0 or 2 and then last one Only One Neg.
What is the order of this complicated condition?
I've tried this:
if (a < 0) != (b < 0):
and it's ok but I'm trying to understand why above doesn't work.

You need parentheses due to operator precedence
def print_if_neg (a,b):
if (a < 0) != (b < 0):
print "Only One Neg"
else:
print "0 or 2"

As CoryKramer pointed out, the operator precedence is making the difference.
Your code is equivalent to this:
def print_if_neg (a,b):
if a < (0 != b) < 0:
print "Only One Neg"
else:
print "0 or 2"
Because != has higher precedence than < by language definition.
So, use () to force the precedence that you need:
def print_if_neg (a,b):
if (a < 0) != (b < 0):
print "Only One Neg"
else:
print "0 or 2"
Also, FYI you are coding the xor operator.

Due to operator precedence you need to place the two conditions in parentheses for your expected results. Otherwise the comparison operators are solved, checking for 0 != b in your code, which is not what you expect.
def print_if_neg (a,b):
if (a < 0) != (b < 0):
print ("Only One Neg")
else:
print ("0 or 2")
print_if_neg(1,1)
print_if_neg(-1,1)
print_if_neg (1,-1)
print_if_neg(-1,-1)
Note that all comparison operators have the same precedence and comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y AND y <= z

This is because condition a < 0 != b < 0 means a < 0 AND 0 != b AND b < 0 First of all when a >= 0 first condition evaluates to False and so nothing else gets evaluated. Then, if a is <0 but b=1 last condition in the chain is False. Therefore your chained condition is False 3 out of 4 times.
This is well explained in section 6.10 of Python documentation.

From this, you could make it more readable imo:
from operator import xor
def print_if_neg (a, b):
if xor(a < 0, b < 0):
print "Only One Neg"
else:
print "0 or 2"

Related

Short-circuiting a condition statement

I wrote a function:
# given a n x m grid return how many different ways there are to move from top left to
# bottom right by only being able to move right or down
def grid(n, m, memo = {}):
if f'{n},{m}' in memo:
return memo[f'{n},{m}']
if n == 1 and m == 1:
return 1
if n == 0 or m == 0:
return 0
memo[f'{n},{m}'] = grid(n,m-1,) + grid(n-1,m)
return grid(n,m-1,) + grid(n-1,m)
Recently I read a bit about short-circuiting in Python and I am trying to understand it further.
As I understand it does not provide any boost in runtime, just sort of syntax sugar.
For example:
1 < 2 < 3 # is True
1 < 2 and 2 < 3 # is also True
# hence
(1 < 2 < 3) == 1 < 2 and 2 < 3 # is True
I was wondering can I write my function with this kind of short-circuiting in my if statements?
I came up with this:
def grid(n, m, memo = {}):
if f'{n},{m}' in memo:
return memo[f'{n},{m}']
if (n or m) == 1:
return 1
if (n and m) == 0:
return 0
memo[f'{n},{m}'] = grid(n,m-1,) + grid(n-1,m)
return grid(n,m-1,) + grid(n-1,m)
Is there any smarter way of using the short-circuit here?
(1 < 2 < 3) is not short-circuiting - I think you misunderstood the meaning of the term. You are correct that it is merely syntax sugar - although it can produce some very weird results in obscure cases. (1 < 2 < 3) expands to (1 < 2) and (2 < 3) - the middle operand is copied to both and and is used for the joining operator.
Short circuiting occurs when python already knows the answer to a boolean expression, even before calculating both the inputs. For example
def false():
print("false")
return False
def true():
print("true")
return True
print(false() and true())
The output would be
false
False
Because when python sees False and, it already knows that the result is False (because and requires both operands to be True), so it doesn't bother running the second half of the line.
This real short circuiting does result in a performance boost, but since you can't turn off short circuiting, it doesn't really matter ¯\_(ツ)_/¯
if (n or m) == 1 is definitely not the same thing as if n == 1 or m == 1. The first if statement is equivalent to:
value = n
if not value:
value = m
if value == 1:
# do something:
Or expressed more succinctly:
if (n if n else m) == 1:
# do something
In other words, n or m only evaluates m if n is False (or 0 if n is an integer), otherwise the result of the expression is n.
What you want to avoid redundancy is:
if 1 in (n, m): # equivalent to: if 1 is either n or m:
Update: Demo
n = 4
m = 1
if (n or m) == 1:
print('if branch taken')
else:
print('else branch taken')
Prints:
else branch taken
if (n or m) == 1
evaluates to
if (<bool>) == 1 # e.g. if {True,False} == 1
which is probably not what you want, since it is essentially evaluating the truthiness of n or m.
Your existing code already captures the nature of short-circuiting;
if n == 1 and m == 1
will only evaluate the second argument m == 1 iff n == 1, or will otherwise short-circuit.
To your comment
As I understand it does not provide any boost in runtime, just sort of syntax suggar.
Well, actually it does provide a runtime boost if Python is able to skip evaluating what would otherwise be "expensive" conditions to evaluate because it is able to short-circuit early.

Loop not getting executed properly

I am trying to create a function which counts the length of elements in the list and then run an if / elif loop on them:
k_nearest_samples_class = training_data[sorted_indices[:k]][:, -1]
# print(k_nearest_samples_class)
# counting number of occurrences of either 0's or 1's with below 2 lines
class_0_count = len(k_nearest_samples_class[k_nearest_samples_class == 0])
class_1_count = len(k_nearest_samples_class[k_nearest_samples_class == 1])
class_2_count = len(k_nearest_samples_class[k_nearest_samples_class == 2])
# Combining > & = sign so even in tie-up cases the instance will be classified to malignant - assumed it
# would be okay in order to reduce false positives
if class_0_count >= class_1_count and class_2_count:
print("0", class_0_count)
return 0
elif class_1_count >= class_0_count and class_2_count:
print("1", class_1_count)
return 1
else:
print("2", class_2_count)
return 2
Giving input one by one like:
[0.0]
[1.0]
[2.0]
currently, my if loop is working illogically.
This line:
if class_0_count >= class_1_count and class_2_count:
is equivalent to:
if class_0_count >= class_1_count and class_2_count > 0:
you need to change it to:
if class_0_count >= class_1_count and class_0_count >= class_2_count:
or you can compare with the maximum value of the two:
if class_0_count >= max(class_1_count, class_2_count):
Expanding on #MoeA's answer above:
A (alternative) Pythonic way to perform this test is to use the all() function like:
if all([class_0_count >= class_1_count, class_0_count >= class_2_count]):
...

Optimizing if-elif expressions in Python

I'm trying to optimize my code by using dictionaries instead of if-elif statements.
I've read that you can optimize code by using dictionaries instead of if-elif statements, but I don't know how to do that. I'd like to use the logical expressions below somehow in the dictionary. (The code iterates through a and b)
def e_ha(n, t, a, b, E):
if a == b:
return 6
elif (a%n == 0, a != n**2, b == a + 1) == (True, True, True):
return 0
elif ((a-1)%n == 0, (a-1) != n**2, b == a - 1) == (True, True, True):
return 0
elif (a%n == 0, b == a-(n-1)) == (True, True):
return 1
elif (b%n == 0, a == b-(n-1)) == (True, True):
return 1
elif abs(a-b) == 1:
return 1
elif abs(a-b) == n:
return 1
else:
return 0
One naive approach to achieve the best performance is to build a big table storing the results for all possible (a, b) pairs. However, this could consume lots of memory and becomes inpractical for large ns.
Here is how the code can be optimized using a normal approach, as explained in the following step-by-step.
1. Using Explicit and for Logical Expressions
As suggested in the comments, this is much more readable and also more efficient because of the short circuiting behavior of and. This change alone reduces the runtime by 60% in my tests.
2. Remove Redundant Conditions
Since both a and b range from 1 to n**2, if a == n**2, then b == a + 1 can never be fulfilled. Therefore the a != n**2 check in the condition a%n == 0 and a != n**2 and b == a + 1 is redundant. The same applies to the third condition. Eliminating them simplifies these conditions to:
...
elif a % n == 0 and b == a + 1:
elif (a - 1) % n == 0 and b == a - 1:
...
3. Avoid Repeated Computations in Conditions
Note that the above-improved conditions
a % n == 0 and b == a + 1 and (a - 1) % n == 0 and b == a - 1 are special cases of abs(a - b) == 1. Therefore these conditions can be rewritten using nested if-else as follows.
if abs(a - b) == 1:
if a % n == 0 and b > a: return 0
elif b % n == 0 and a > b: return 0 # a - 1 equals to b here so it is replaced to save one computation
else return 1
Also note that the value abs(a - b) is related to all the conditions. Therefore it can be computed before all conditions are checked. With this change, the code becomes
d = abs(a - b)
if d == 0: return 6
elif d == 1:
if a % n == 0 and b > a: return 0
elif b % n == 0 and a > b: return 0
else return 1
elif d == n - 1:
if a % n == 0 and a > b: return 1
elif b % n == 0 and b > a: return 1
else return 0
elif d == n: return 1
else: return 0
4. Simplify Logic
For example, the first nested if-else above can be simplified to
if min(a, b) % n == 0: return 0
else return 1
A more compact syntax is:
return 1 if min(a, b) % n == 0 else 0
5. Apply Python-specific Optimizations
In Python, the number 0 is regarded as having a falsy value. So for numbers if d != 0: and if d == 0: are equivalent to if d: and if not d: respectively. The latter is a bit faster. Applying this change results in the following optimized code (here a more compact syntax is used to shorten the answer).
d = abs(b - a)
if not d: return 6
elif d == 1: return 1 if min(a, b) % n else 0
elif d == n - 1: return 0 if max(a, b) % n else 1
else: return 1 if d == n else 0
Applying steps 2 to 5 above reduces the runtime by another 50%.
6. Adjust Order of Conditions based on Input Distribution
This change relies on the knowledge of the actual input distribution in the application. The target is to make the more frequently seen inputs return faster. In this example, assume the inputs a and b are uniformly distributed within [1, n**2] and n >= 10. In this case, the most frequent scenario is that the value d does not match any of the if conditions and 0 is returned at the end after all conditions are checked. In order to speedup, we can make it fail faster by first checking whether d can possibly lead to a non-zero return value.
d = abs(a - b)
if 1 < d < n - 1 or d > n: return 0 # Return 0 if d is not in [0, 1, n - 1, n]
elif d == 1: return 1 if min(a, b) % n else 0
elif d == n - 1: return 0 if max(a, b) % n else 1
else: return 1 if d == n else 6 # d == 0 case is moved to the last "else" since it is least frequently seen
7. Using Lookup Tables
Further speedup can be achieved by using lookup tables. Here, the values [0, 1, n - 1, n] for the first conditional check can be stored to speedup the check. In this case, there are two primary options for this: a set or a length-n+1 list of boolean values. The former uses less memory while the latter has better performance. Note that the lookup table should be constructed once outside the function and passed into it. The code using a boolean list as a lookup is as follows:
def func(n, a, b, lookup):
d = abs(a - b)
if not (d <= n and lookup[d]): return 0
...
Applying steps 6 and 7 (with boolean list lookup) reduces the runtime by another 15%.
Note that in this example a 2D lookup table (implemented as nested lists or dictionaries) can also be applied using (min(a, b) % n, d) as indices. However, under the same assumption of input distribution in step 6, this is slightly slower than a 1D lookup because of the overhead of one extra level of indexing.
The runtime above is the total time of applying the function to all possible (a, b) values within [1, n**2] for n=20.
Using a dictionary, where the keys are boolean expressions is not going to work the way you hope it does. There is no such thing as a boolean-expression-object that could take the place of the key, only booleans. In the end, boolean expressions evaluate to either True or False, so at most you could only have two key-value pairs.
I would suggest, however, you make things a bit more readable/pythonic:
if a%n == 0 and a != n**2 and b == a + 1:
or
if all((a%n == 0, a != n**2, b == a + 1)):
You can just use a list of tuples and loop through it:
def e_ha(n, t, a, b, E):
checks = [
(a == b, 6),
(all(a%n == 0, a != n**2, b == a + 1), 0 ),
(all((a-1)%n == 0, (a-1) != n**2, b == a - 1), 0),
(all(a%n == 0, b == a-(n-1)), 1),
(all(b%n == 0, a == b-(n-1)), 1 ),
(abs(a-b) == 1, 1),
(abs(a-b) == n, 1),
(true, 0)
]
for valid, return_value in checks:
if valid:
return return_value
Caveat:
This is most certainly not faster in any way. Timed it multiple times and it was always slower.
It is less readable than the alternative

Python-Why aren't my elif statements not getting evaluated in this recursive multiplication which takes into account negative numbers

Hi I have looked up a few recursive multiplication of 2 numbers in python, but none of them take into account the possibility of negative numbers or if they do they don't explain the entire steps.
I have coded the following the program but I am getting an error, can someone please let me know what is wrong with it?
def mult(a,b):
if a==0 | b==0:
return 0
elif a==1:
return b
elif a > 0 & b >0:
return b + mult(a-1,b)
elif a < 0 & b > 0:
return - b + mult(a+1,b))
elif a > 0 & b < 0:
return - b + mult(a-1, b))
else:
return -b + mult(a+1, b)
print(mult(-4,5))
| and & are bitwise operators, not logical operators, and their (relatively) high precedence means that a > 0 & b >0 is parsed as a > (0 & b) > 0, which is not what you want. Use or and and instead.
You have some python syntax errors and some sign problems. This works for mult(-4,5) and mult(5,-4).
def mult(a,b):
if a == 0 or b == 0:
return 0
elif a == 1:
return b
elif b == 1:
return a
elif a >0 and b > 0:
return b + mult(a-1,b)
elif a < 0 and b > 0:
return -b+mult(a+1,b)
elif a > 0 and b < 0:
return b+mult(a-1,b)
else:
return b + mult(a+1,b)
In your elif statement, you're using a bitwise "and" operator rather than the logical and operator. Everywhere that you have "&" replace it with "and"

function return none instead of return number

I create a function that compare with x and y variable. Inside the function has a lots of nested elif to compare the x and y then return integer. The problem is right now, when it runs at the certain elif statement, it didn't execute the statement although the statement is correct.
def convertTo(self, x, y):
if( x == 0 & y == 0):
return 0
if( x == 0 & y == 1):
return 1
if( x == 0 & y == 2):
return 2
if( x == 0 & y == 3):
return 3
if( x == 1 & y == 0):
return 4 # Didn't return this line even though x = 1 and y = 0
else
return None
def main():
self.convertTo(0,0)
self.convertTo(0,1)
self.convertTo(0,2)
self.convertTo(0,3)
self.convertTo(1,0) # return None? Why?
You're performing a chained equality comparison which is not doing what you think it does. The bitwise & is performed first as it has a higher priority than ==.
Replace:
x == 1 & y == 0
# 1 == 1 & 0 == 0
# 1 == 0 == 0 False!
With:
x == 1 and y == 0
See: Operator precedence
In Python, "&" and "and" do two different things. "and" is what you should be using, "&" is a binary operator.
if
a = 0011 1100
and
b = 0000 1101
then
a&b = 0000 1100
See http://www.tutorialspoint.com/python/python_basic_operators.htm
You should use and instead of &, as & is a bitwise and.
Chaining multiple conditions in Python is generally done with an if-elif-else statement like below:
if a and b:
# a and b both was true
elif a and not b:
# a was true, but b wasn't
else:
# none of the conditions matched
In your code, if it wasn't for the return statement in each if, and the fact that you are checking the same two variables, it would be possible for two if statements to evaluate to true.
if a:
# this will run if a was true
if b:
# regardless of a this will run if b was true
else:
# regardless of a this will only run if b was false
Also, take a look at this: https://docs.python.org/3/tutorial/controlflow.html

Categories

Resources