Chained comparison (a < x < b) in 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 tox < y and y <= z
, except thaty
is evaluated only once (but in both casesz
is not evaluated at all whenx < 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 toa 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