0%

17- Chaining Comparison Operators

Python lets you write comparisons the way mathematicians do. One value between two boundaries. One line. No extra words.

In most programming languages, checking if a number falls within a range requires two separate comparisons joined by and. Like this: if x > 1 and x < 10. Python is different. Python lets you chain comparison operators naturally, the way you would write them in mathematics: if 1 < x < 10. This is not just syntactic sugar. It is more readable, more intuitive, and even has subtle performance benefits. Chaining works with all comparison operators and can include multiple links in a single expression.

🕯️ Magic Note

Chained comparisons are evaluated from left to right, but each expression in the middle is evaluated only once. This is different from writing a < b and b < c where b could be evaluated twice if it is a function call. Python’s chaining is both elegant and efficient.

Basic Number Range Checks
The most common use of chained comparisons is checking if a number falls within a specific range.

Python

score = 75

# Check if score is between 60 and 80

if 60 <= score <= 80:

print(“Good score!”)

# Check if score is strictly between 50 and 100

if 50 < score < 100:

print(“Score is in range”)

temperature = 22

if 18 <= temperature <= 25:

print(“Comfortable temperature”)

💡 Chained comparisons are perfect for validating user input, checking if values are within expected ranges, and writing clean boundary conditions.
Chaining Different Operators
You can mix different comparison operators in a chain. Python evaluates them all, and the entire chain returns True only if every comparison is True.

Python

x = 5

y = 10

z = 15

# Mixing operators

print(x < y <= z) # True (5 < 10 is True, 10 <= 15 is True)

print(x == y < z) # False (x == y is False, so chain fails)

print(x < y != z) # True (5 < 10 is True, 10 != 15 is True)

print(x < y >= z) # False (5 < 10 is True, but 10 >= 15 is False)

🕯️ Magic Note

Python does not limit you to using the same operator throughout the chain. a < b == c > d is perfectly valid. It evaluates to True only if all four comparisons are True. This is powerful but can become hard to read. Use with care.

Chaining with Function Calls
A key advantage of chaining is that expressions in the middle are evaluated only once. This matters when you call functions that have side effects or are expensive to compute.

Python

# A function that prints when called

def get_middle_value():

print(“get_middle_value was called”)

return 5

# Using chaining (function called once)

result = 1 < get_middle_value() < 10

# Output: get_middle_value was called (only once)

print(result) # True

# Without chaining (function called twice if first comparison is True)

temp = get_middle_value()

result = 1 < temp and temp < 10

# But careful: this also evaluates the function once if stored in variable

⚠️ In the non-chained version, if you write 1 < get_middle_value() and get_middle_value() < 10, the function would be called twice. This could be a problem if the function has side effects or is computationally expensive. Chaining avoids this issue.
Chaining with Assignment Expressions (Walrus Operator)
Python 3.8 introduced the walrus operator := which allows assignment inside expressions. This can be combined with chained comparisons for elegant code.

Python

# Using walrus operator in a chain

if 1 < (x := get_value()) < 10:

print(f”x is {x} and it is between 1 and 10″)

# Without walrus, you would need multiple lines

x = get_value()

if 1 < x < 10:

print(f”x is {x} and it is between 1 and 10″)

Chaining with Strings
Chained comparisons also work with strings. You can check alphabetical ordering naturally.

Python

word = “magic”

# Check if word comes between “apple” and “zebra” alphabetically

if “apple” < word < “zebra”:

print(f”{word} is in the dictionary range”)

# Chained string comparisons

print(“a” < “b” < “c”) # True

print(“cat” < “dog” < “eagle”) # True

print(“apple” > “banana” > “cherry”) # False (apple > banana is False)

Chaining with Lists and Tuples
Sequences like lists and tuples can also be compared in chains. Python compares them element by element.

Python

# List comparisons in a chain

print([1, 2] < [1, 2, 3] < [1, 2, 4]) # True

print([1, 3] > [1, 2] > [1, 1]) # True

# Tuple comparisons

print((1, 2) < (1, 3) < (2, 0)) # True

Short-Circuit Behavior
Chained comparisons short-circuit. As soon as any comparison in the chain is False, Python stops evaluating the rest. This can improve performance and prevent errors.

Python

def risky_operation():

print(“Risky operation was called”)

return 100

x = 5

# First comparison (x > 10) is False, so risky_operation() never runs

if x > 10 < risky_operation():

print(“This won’t print”)

# risky_operation() is NOT called (short-circuit)

# However, the middle value in a chain is not short-circuited the same way

# In 1 < x < 10, x is evaluated once regardless

🕯️ Magic Note

Short-circuiting in chained comparisons is subtle. In a < b < c, b is always evaluated. But if a < b is False, then b < c is never evaluated. This matches how and short-circuits.

Chaining with is and is not
You can chain is and is not operators, but this is less common. Use with care as it can be confusing.

Python

a = [1, 2]

b = a

c = a

# Chaining is operators

print(a is b is c) # True (all refer to same object)

x = “hello”

y = “hello”

z = “hello”

# May be True due to string interning, but not guaranteed

print(x is y is z) # May be True (implementation dependent)

Parentheses in Chained Comparisons
You can use parentheses to group parts of a chain, but be careful. Parentheses can break the chaining behavior.

Python

x = 5

# Normal chaining

print(1 < x < 10) # True

# With parentheses – changes meaning

print(1 < (x < 10)) # 1 < True (True becomes 1) → False

print((1 < x) < 10) # True < 10 (True becomes 1) → True

# Parentheses are useful for complex conditions though

print(1 < x and (x < 5 or x > 8)) # x=5 → False

⚠️ Do not add parentheses around the middle of a chained comparison unless you intend to change the meaning. 1 < (x < 10) compares 1 to a boolean, which is not what you want. Python compares booleans to numbers because True == 1 and False == 0.
Common Use Cases
Here are the most practical applications of chained comparisons.

Python

# 1. Input validation

age = int(input(“Enter age: “))

if 0 <= age <= 120:

print(“Valid age”)

# 2. Grade boundaries

score = 85

if 90 <= score <= 100:

grade = “A”

elif 80 <= score < 90:

grade = “B”

# 3. Time range checks

hour = 14

if 9 <= hour <= 17:

print(“Working hours”)

# 4. Boundary conditions

value = 0

if -1 < value < 1:

print(“Value is near zero”)

Common Mistakes with Chained Comparisons
  • Using parentheses incorrectly: 1 < (x < 10) compares 1 to a boolean
  • Thinking all operators in a chain must be the same (they do not)
  • Forgetting that a < b > c is valid but can be less readable than a < b and b > c
  • Using chained comparisons with incompatible types (TypeError)
  • Writing x == y == z when you meant x == y and y == z (this actually works, but be clear about intent)
Check Your Understanding
  • Write a chained comparison that checks if x is between 10 and 20 (inclusive).
  • What is the difference between 1 < x < 10 and 1 < (x < 10)?
  • Does a < b > c mean the same as a < b and b > c?
  • What happens if the first comparison in a chain is False?
  • Write a chained comparison that checks if a string comes between “apple” and “zebra” alphabetically.

⚡ Whisper

Mathematicians have written 1 < x < 10 for centuries. Programmers in other languages cannot. They must write 1 < x and x < 10. Python chose the path of the mathematician. The path of natural thinking. The path of reading code the way you read a sentence. “If x is greater than 1 and less than 10” becomes “if 1 is less than x is less than 10.” This is not a trick. This is Python respecting how humans already think. Chain your comparisons. Write code that reads like a story. Your future self will understand it instantly. That is the conjure of clarity.

Related posts