user2933244
user2933244

Reputation:

Given 2 int values, return True if one is negative and other is positive

def logical_xor(a, b): # for example, -1 and 1
    print (a < 0) # evaluates to True
    print (b < 0) # evaluates to False
    print (a < 0 != b < 0) # EVALUATES TO FALSE! why??? it's True != False
    return (a < 0 != b < 0) # returns False when it should return True

print ( logical_xor(-1, 1) ) # returns FALSE!

# now for clarification

print ( True != False) # PRINTS TRUE!

Could someone explain what is happening? I'm trying to make a one liner:

lambda a, b: (a < 0 != b < 0)

Upvotes: 25

Views: 7241

Answers (4)

haccks
haccks

Reputation: 106002

In Python, comparison operators are of the same precedence, and they are non-associative. There is a separate rule for sequences of comparison operators, the chaining rule. Python documentation states about that:

if a, b, c, ..., y, z are expressions and op1, op2, ..., opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z, except that each expression is evaluated at most once.

Further, a op1 b and b op2 c and ... y opN z evaluates left to right.

 a < 0 and 0 != b and b < 0  

a < 0 will evaluated to False, and the further evaluation will be stopped due to short-circuit evaluation. So, the whole expression will be evaluated as False.

Upvotes: 0

tzaman
tzaman

Reputation: 47770

All comparison operators in Python have the same precedence. In addition, Python does chained comparisons. Thus,

(a < 0 != b < 0)

breaks down as:

(a < 0) and (0 != b) and (b < 0)

If any one of these is false, the total result of the expression will be False.

What you want to do is evaluate each condition separately, like so:

(a < 0) != (b < 0)

Other variants, from comments:

(a < 0) is not (b < 0) # True and False are singletons so identity-comparison works

(a < 0) ^ (b < 0) # bitwise-xor does too, as long as both sides are boolean

(a ^ b < 0) # or you could directly bitwise-xor the integers; 
            # the sign bit will only be set if your condition holds
            # this one fails when you mix ints and floats though

(a * b < 0) # perhaps most straightforward, just multiply them and check the sign

Upvotes: 30

itzMEonTV
itzMEonTV

Reputation: 20339

You can use this

return (a < 0) != (b < 0)

Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

So it becomes

(a < 0) and (0 != b) and (b < 0)

See https://docs.python.org/3/reference/expressions.html#not-in

Upvotes: 4

EvenLisle
EvenLisle

Reputation: 4812

Your code doesn't work as intended because != takes higher precedence than a < 0 and b < 0. As itzmeontv suggests in his answer, you can simply decide the precedence yourself by surrounding logical components with parentheses:

(a < 0) != (b < 0)

Your code attempts to evaluate a < (0 != b) < 0

[EDIT]

As tzaman rightly points out, the operators have the same precedence, but your code is attempting to evaluate (a < 0) and (0 != b) and (b < 0). Surrounding your logical components with parentheses will resolve this:

(a < 0) != (b < 0)

Operator precedence: https://docs.python.org/3/reference/expressions.html#operator-precedence

Comparisons (i.a. chaining): https://docs.python.org/3/reference/expressions.html#not-in

Upvotes: 7

Related Questions