Pythonで複数の比較演算子を連結して記述(a < x < bなど)

Modified: | Tags: Python

Pythonでは比較演算子を使った比較を連結して記述できる。a < x and x < bを数学のようにa < x < bと書くことが可能。

複数の比較の連結

a < x < bは各比較部分がandで連結されたa < x and x < bと等価。

比較はいくらでも連鎖することができます。例えば x < y <= zx < y and y <= z と等価になります。ただしこの場合、前者では y はただ一度だけ評価される点が異なります (どちらの場合でも、 x < y が偽になると z の値はまったく評価されません)。

形式的には、 a, b, c, ..., y, z が式で op1, op2, ..., opN が比較演算子である場合、 a op1 b op2 c ... y opN za op1 b and b op2 c and ... y opN zと等価になります。ただし、前者では各式は多くても一度しか評価されません。 6. 式 (expression) - 比較 — Python 3.11.3 ドキュメント

a, b, c, ..., y, z が式(変数単独の場合も含む)で op1, op2, ..., opN が比較演算子(<>など)である場合、以下の2つは等価となる。

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

具体例は以下の通り。andを使う場合の書き方も同時に示す。

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

より複雑な例。

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

連結した場合の違い

ドキュメントにあるように、連結して記述した場合、各式は多くても一度しか評価されない。

説明のために、引数をそのまま返す簡単な関数を定義する。関数が呼ばれたことを確認するために内部でprint()を実行している。

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

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

比較を連結して記述した場合、該当の関数は一度しか呼ばれない。

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

andを使った場合、該当の関数は二度呼ばれる。

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

なお、X and Yでは、XFalseだとYは評価されないため、以下のような場合は連結してもしなくても該当の関数は一度だけ呼ばれる。

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

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

このような仕組みをショートサーキット(短絡評価)と呼ぶ。

いずれにせよ、連結して記述した場合は各式は多くても一度しか評価されないので、複雑な処理を行う関数などの結果をそのまま比較する場合は連結して記述した方が無駄がない。

活用例: 数値の範囲

比較の連結が便利なのは数値の範囲を条件とする場合。

これまでの例でも示した通り。

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

活用例: 複数の変数・式がすべて等しい

もう一つ便利なのは、複数の変数・式がすべて等しいかどうかを判定する場合。

値が等価であるかを判定する比較演算子==で連結すると、すべての値が等しい場合にのみTrueとなる。

a = 10
b = 10
c = 10

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

ひとつでも違う値があるとFalse

a = 10
b = 1
c = 10

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

値が等価でないときにTrueを返す比較演算子!=を使うときは注意が必要。すべての値の組み合わせが評価されるわけではないので、等価な値の組み合わせがあっても順番によってはTrueを返す。

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

複数の値がすべて異なる(ユニークである)ことを判定するには、リストに格納してから重複を判定する方法がある。以下の記事を参照。

なお、==, !=は値の比較。オブジェクトの同一性を比較するにはis, is notを使う。

例えば、整数intと浮動小数点数floatを比較する場合、値が等価であれば==Trueを返すが、異なるオブジェクトなのでisFalseを返す。

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

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

print(i == f)
# True

print(i is f)
# False

使いすぎに注意

文法的にはいろいろな書き方ができるが、上述の、

  • 数値の範囲
  • 複数の値が等しいか

の判定以外で比較の連結を使うとコードが読み取りにくくなるので注意。

例えば、リストなどに要素が含まれているかを判定する演算子inも連結できるが、おそらく多くの人にとっては分かりにくい。「各式が一度しか評価されない」という点に強いメリットのある状況でもない限り、andを使ったほうがいいだろう。

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

print(50 < a in l)
# True

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

関連カテゴリー

関連記事