Chained comparison (a < x < b) in Python

Modified: | Tags: Python

In Python, comparisons can be chained. You can write a < x and x < b as a < x < b like in mathematics.

Chain multiple comparisons

For example, a < x < b is equivalent to a < x and x < b.

Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

Formally, if a, b, c, …, y, z are expressions and op1, op2, …, opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z, except that each expression is evaluated at most once. 6. Expressions - Comparisons — Python 3.11.3 documentation

If a, b, c, ..., y, z are expressions and op1, op2, ..., opN are comparison operators, such as < or >, the following two are equivalent:

a op1 b op2 c ... y opN z
a op1 b and b op2 c and ... y opN z

Here are some specific examples:

x = 15
print(10 < x < 20)
# True

print(10 < x and x < 20)
# True

x = 0
print(10 < x < 20)
# False

print(10 < x and x < 20)
# False

And more complex examples:

x = 15
y = 25

print(10 < x < 20 < y < 30)
# True

print(10 < x and x < 20 and 20 < y and y < 30)
# True

x = 15
y = 40

print(10 < x < 20 < y < 30)
# False

print(10 < x and x < 20 and 20 < y and y < 30)
# False

As mentioned in the official documentation, each expression is evaluated at most once when chained.

To demonstrate this, define a simple function that returns its arguments as is and use print() to confirm that the function has been called.

def test(x):
    print('function is called')
    return(x)

print(test(15))
# function is called
# 15

If the comparisons are chained, the function is called only once.

print(10 < test(15) < 20)
# function is called
# True

In contrast, when using and, the function is called twice:

print(10 < test(15) and test(15) < 20)
# function is called
# function is called
# True

In X and Y, if X is False, Y is not evaluated. Therefore, in the following case, the function is called only once, whether it is chained or not:

print(10 < test(0) < 20)
# function is called
# False

print(10 < test(0) and test(0) < 20)
# function is called
# False

This is known as short-circuit evaluation. See the following article for details.

In any case, chaining is more efficient when comparing the results of complex functions, as each expression is evaluated only once at most.

Example 1: Numerical range

Chained comparisons are useful when working with numerical ranges:

x = 15

if 10 < x < 20:
    print('result: 10 < x < 20')
else:
    print('result: x <= 10 or 20 <= x')
# result: 10 < x < 20

x = 30

if 10 < x < 20:
    print('result: 10 < x < 20')
else:
    print('result: x <= 10 or 20 <= x')
# result: x <= 10 or 20 <= x

Example 2: Check if multiple values are all equal

Another handy use case is checking if multiple variables and expressions are all equal.

Using the comparison operator == in a chain returns True only if all values are equal:

a = 10
b = 10
c = 10

if a == b == c:
    print('all equal')
else:
    print('not all equal')
# all equal

If any of the values are different, False will be returned.

a = 10
b = 1
c = 10

if a == b == c:
    print('all equal')
else:
    print('not all equal')
# not all equal

Be careful when using the comparison operator !=, which returns True when the values are not equivalent. Since not all combinations of values are evaluated, True may be returned even if there are equivalent values, depending on the order.

a = 10
b = 1
c = 100

print(a != b != c)
# True

a = 10
b = 10
c = 1

print(a != b != c)
# False

a = 10
b = 1
c = 10

print(a != b != c)
# True

One way to check if multiple values are all unique is to store them in a list and then check for duplicates. See the following article.

Note that == and != compare values. Use is and is not to compare the identity of objects.

For example, when comparing an integer int with a floating point number float, == returns True if the values are equivalent, but is returns False because they are different objects.

i = 10
print(type(i))
# <class 'int'>

f = 10.0
print(type(f))
# <class 'float'>

print(i == f)
# True

print(i is f)
# False

Be careful not to overuse it

Chained comparisons can be written in various ways, but be careful that the code can be difficult to read in some cases.

For example, the in operator, which tests if a list contains a particular element, can also be chained. However, this might be confusing for most people. Unless there is a significant advantage in evaluating each expression only once, it is better to use and.

a = 100
l = [0, 10, 100, 1000]

print(50 < a in l)
# True

print(50 < a and a in l)
# True

Related Categories

Related Articles