6
votes

I have a line of code in my script that has both these operators chained together. From the documentation reference BOOLEAN AND has a lower precedence than COMPARISON GREATER THAN. I am getting unexpected results here in this code:

>>> def test(msg, value):
...     print(msg)
...     return value

>>> test("First", 10) and test("Second", 15) > test("Third", 5)
First
Second
Third
True

I was expecting Second or Third test to happen before the fist one, since > operator has a higher precedence. What am I doing wrong here?

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

2
The boolean interpretation of your first value, 10, is also True. Therefore, 10 and 15 > 5 == 10 and (15 > 5) == 10 and True == True - Finwood
But why did the interpreter get 10 before it did 15 or 5? Shouldn't the comparison happen before boolean checks? - Renae Lider
test("First", 10) and test("Second", 15) > test("Third", 5) is equivalent to test("First", 10) and (test("Second", 15) > test("Third", 5)) Also Python evaluates and lazily - Alik
Well the precedence is definitely correct. 0 and 0 > -1 is different from (0 and 0) > -1. My guess is that while the values returned are evaluated in the correct order, the functions are not called in that order for some reason. What's interesting is that replacing 10,15,5 with 0,0,-1 only prints First. Maybe it's a compiler optimization - since in your case, as @Alik pointed out, both orders result in the same value. - pushkin

2 Answers

7
votes

Because you are looking at the wrong thing. call (or function call) takes higher precendence over both and as well as > (greater than) . So first function calls occur from left to right.

Python will get the results for all function calls before either comparison happens. The only thing that takes precendence over here would be short circuiting , so if test("First",10) returned False, it would short circuit and return False.

The comparisons and and still occur in the same precendence , that is first the result of test("Second", 15) is compared against test("Third", 5) (please note only the return values (the function call already occured before)) . Then the result of test("Second", 15) > test("Third", 5) is used in the and operation.

From the documentation on operator precedence -

enter image description here

3
votes

One way to see what's happening is to look at exactly how Python is interpreting this result:

>>> x = lambda: test("First", 10) and test("Second", 15) > test("Third", 5)
>>> dis.dis(x)
  1           0 LOAD_GLOBAL              0 (test)
              3 LOAD_CONST               1 ('First')
              6 LOAD_CONST               2 (10)
              9 CALL_FUNCTION            2
             12 JUMP_IF_FALSE_OR_POP    42
             15 LOAD_GLOBAL              0 (test)
             18 LOAD_CONST               3 ('Second')
             21 LOAD_CONST               4 (15)
             24 CALL_FUNCTION            2
             27 LOAD_GLOBAL              0 (test)
             30 LOAD_CONST               5 ('Third')
             33 LOAD_CONST               6 (5)
             36 CALL_FUNCTION            2
             39 COMPARE_OP               4 (>)
        >>   42 RETURN_VALUE        

If you do the same for 10 and 15 > 5, you get:

>>> x = lambda: 10 and 15 > 5
>>> dis.dis(x)
  1           0 LOAD_CONST               1 (10)
              3 JUMP_IF_FALSE_OR_POP    15
              6 LOAD_CONST               2 (15)
              9 LOAD_CONST               3 (5)
             12 COMPARE_OP               4 (>)
        >>   15 RETURN_VALUE