Python
Comparison Operators
Programming Tips
Code Simplification
Python Best Practices

Simplify Chained Comparison

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Python lets you write comparisons the same way you would on a whiteboard: a < b < c instead of a < b and b < c. This feature, called chained comparison, makes range checks and ordering assertions shorter and easier to read. It also avoids evaluating the middle expression twice, which matters when that expression is a function call with side effects. This article explains how Python evaluates chained comparisons under the hood, demonstrates practical examples, and contrasts the behavior with other popular languages.

How Python Evaluates Chained Comparisons

When Python encounters a < b < c, it does not treat it as (a < b) < c. Instead it rewrites it internally as:

python
a < b and b < c

with the important guarantee that b is evaluated only once. If the first comparison is False, Python short-circuits and never evaluates c at all.

Here is a concrete demonstration:

python
1x = 3
2# Check that x is between 1 and 10 (exclusive)
3if 1 < x < 10:
4    print("x is in range")  # This prints

You can chain more than two operators:

python
a, b, c, d = 1, 2, 3, 4
print(a < b < c < d)   # True
print(a < b > c < d)   # False, because b > c is False (2 > 3)

Mixing Different Operators

Chained comparisons are not limited to <. You can mix any comparison operators, including <=, >=, ==, !=, is, is not, in, and not in. Each adjacent pair is joined by an implicit and.

python
1# Combining equality and ordering
2x = 5
3print(1 < x == 5)       # True  -> (1 < x) and (x == 5)
4print(1 < x == 6)       # False -> (1 < x) and (x == 6)
5
6# Using 'is' in the chain
7a = b = []
8print(a is b is not None)  # True -> (a is b) and (b is not None)

Range Checks in Practice

The most common use case is validating that a value falls inside a range:

python
1def classify_temperature(temp):
2    if temp < 0:
3        return "freezing"
4    elif 0 <= temp < 20:
5        return "cold"
6    elif 20 <= temp < 30:
7        return "comfortable"
8    else:
9        return "hot"
10
11print(classify_temperature(25))  # "comfortable"

Without chaining you would write temp >= 20 and temp < 30, which repeats temp and is slightly harder to scan.

Single Evaluation of the Middle Expression

Because the middle operand is evaluated only once, chaining is safer when the expression has side effects:

python
1call_count = 0
2
3def expensive():
4    global call_count
5    call_count += 1
6    return 5
7
8# Chained: expensive() is called once
9result = 1 < expensive() < 10
10print(result)       # True
11print(call_count)   # 1
12
13# Unchained equivalent would call it twice:
14# result = 1 < expensive() and expensive() < 10  -> call_count would be 2

Contrast with Other Languages

Most languages do not support chained comparisons. In JavaScript, C, C++, and Java, writing 1 < x < 10 compiles and runs, but it does not do what you might expect:

javascript
1// JavaScript
2let x = 15;
3console.log(1 < x < 10);
4// Step 1: (1 < 15) -> true
5// Step 2: (true < 10) -> (1 < 10) -> true  (wrong!)

JavaScript first evaluates 1 < x to true, then coerces true to 1 and compares 1 < 10, which is true even though x is 15. The correct JavaScript equivalent is:

javascript
console.log(1 < x && x < 10); // false, as expected

Ruby and Rust also require the explicit && form. Among mainstream languages, Python is nearly unique in supporting mathematical-style chaining natively.

Common Pitfalls

  • Assuming other languages support chaining. Writing a < b < c in JavaScript or C produces a valid but semantically incorrect expression because the boolean result of the first comparison is coerced to a number before the second comparison.
  • Chaining != and expecting "all different." The expression a != b != c means (a != b) and (b != c), which does not check whether a != c. To verify all three are distinct, you need an explicit check or use len({a, b, c}) == 3.
  • Overcomplicating chains with too many operators. A chain like a < b >= c != d is not e is technically valid but unreadable. Limit chains to straightforward range checks for clarity.
  • Forgetting short-circuit behavior in chains with side effects. If the first comparison is False, subsequent expressions in the chain are never evaluated. Code that relies on those expressions executing (for example, function calls that update state) will silently skip them.
  • Using chained comparisons with incompatible types. In Python 3, comparing unorderable types (like int and str) raises a TypeError. A chain like 0 < "hello" < 10 will fail at the first comparison, and the error message may be confusing if you are not expecting it.

Summary

  • Python chained comparisons rewrite a < b < c as a < b and b < c with b evaluated only once.
  • You can mix any comparison operators (<, <=, ==, !=, is, in, and others) in a single chain.
  • Short-circuit evaluation means later parts of the chain are skipped as soon as one comparison is False.
  • Most other languages (JavaScript, C, Java) do not support this syntax; they coerce the boolean result of the first comparison and produce incorrect results.
  • Keep chains simple -- one or two operators for range checks -- and avoid long chains that sacrifice readability.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.