Chained comparisons
The gotcha
a < b < c is not (a < b) < c — it’s (a < b) and (b < c), with b evaluated only once.
Minimal repro
1 < 2 < 3 # True — works as expected
3 > 2 > 1 # True
1 < 2 > 0 # True — `2 < 1 and 2 > 0`? No: it's `1 < 2 and 2 > 0`
False == False == True # False — equivalent to `False == False and False == True`
# Side-effect surprise:
def b():
print("called")
return 5
1 < b() < 10 # b() called once, prints "called" once
Why it happens
Python’s grammar treats comparisons specially: any sequence of comparison operators < <= > >= == != chains with implicit and. The middle operand is evaluated once, so f() < x < g() calls f and g exactly once each, and short-circuits on the left.
This is unique to comparison operators. a + b + c is just left-associative; a < b < c is not (a < b) < c.
How to avoid
It’s mostly a feature, not a bug — 0 <= idx < len(arr) reads naturally. The trap is when you don’t realize the chaining is happening:
False == False == True
# Reads as "False equals False equals True" → expected True
# Actually: False == False AND False == True → True AND False → False
When in doubt, parenthesize: (False == False) == True → True.
Interview angle
Predict the value of False == False == True or 1 < 2 == 2. Bonus question: “How many times is f() called in f() < g() < f()?” (Answer: twice — once for f() on the left, once for g() in the middle, then f() again on the right.)