How do chained comparisons in Python actually work?

You can simply let Python tell you what bytecode is produced with the dis module:

>>> import dis
>>> def f(): return 1 < input("Value:") < 10
... 
>>> dis.dis(f)
  1           0 LOAD_CONST               1 (1)
              3 LOAD_GLOBAL              0 (input)
              6 LOAD_CONST               2 ('Value:')
              9 CALL_FUNCTION            1
             12 DUP_TOP             
             13 ROT_THREE           
             14 COMPARE_OP               0 (<)
             17 JUMP_IF_FALSE_OR_POP    27
             20 LOAD_CONST               3 (10)
             23 COMPARE_OP               0 (<)
             26 RETURN_VALUE        
        >>   27 ROT_TWO             
             28 POP_TOP             
             29 RETURN_VALUE        

Python uses a stack; the CALL_FUNCTION bytecode uses items on the stack (the input global and the 'Value:' string) to call a function with one argument, to replace those two items on the stack with the result of the function call. Before the function call, the the constant 1 was loaded on the stack.

So by the time input was called the stack looks like:

input_result
1

and DUP_TOP duplicates the top value, before rotating the top three stack values to arrive at:

1
input_result
input_result

and a COMPARE_OP to test the top two items with <, replacing the top two items with the result.

If that result was False the JUMP_IF_FALSE_OR_POP bytecode jumps over to 27, which rotates the False on top with the remaining input_result to clear that out with a POP_TOP, to then return the remaining False top value as the result.

If the result True however, that value is popped of the stack by the JUMP_IF_FALSE_OR_POP bytecode and in it’s place the 10 value is loaded on top and we get:

10    
input_result

and another comparison is made and returned instead.

In summary, essentially Python then does this:

stack_1 = stack_2 = input('Value:')
if 1 < stack_1:
    result = False
else:
    result = stack_2 < 10

with the stack_* values cleared again.

The stack, then, holds the unnamed intermediate result to compare

Leave a Comment