Reputation:
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
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 andop1, op2, ..., opN
are comparison operators, then aop1 b op2 c ... y opN z
is equivalent toa 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
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
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
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