Looking at the disassembly (the bytes codes) it is obvious why `0 < 0 == 0`

is `False`

.

Here is an analysis of this expression:

```
>>>import dis
>>>def f():
... 0 < 0 == 0
>>>dis.dis(f)
2 0 LOAD_CONST 1 (0)
3 LOAD_CONST 1 (0)
6 DUP_TOP
7 ROT_THREE
8 COMPARE_OP 0 (<)
11 JUMP_IF_FALSE_OR_POP 23
14 LOAD_CONST 1 (0)
17 COMPARE_OP 2 (==)
20 JUMP_FORWARD 2 (to 25)
>> 23 ROT_TWO
24 POP_TOP
>> 25 POP_TOP
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
```

Notice lines 0-8: These lines check if `0 < 0`

which obviously returns `False`

onto the python stack.

Now notice line 11: `JUMP_IF_FALSE_OR_POP 23`

This means that if `0 < 0`

returns `False`

perform a jump to line 23.

Now, `0 < 0`

is `False`

, so the jump is taken, which leaves the stack with a `False`

which is the return value for the whole expression `0 < 0 == 0`

, even though the `== 0`

part isn’t even checked.

So, to conclude, the answer is like said in other answers to this question.

`0 < 0 == 0`

has a special meaning. The compiler evaluates this to two terms: `0 < 0`

and `0 == 0`

. As with any complex boolean expressions with `and`

between them, if the first fails then the second one isn’t even checked.

Hopes this enlightens things up a bit, and I really hope that the method I used to analyse this unexpected behavior will encourage others to try the same in the future.