Pythonの短絡評価について

ifの条件式のなかでandやorを使ったことがあると思いますが、これはPythonではどのような仕組みで実現されているのでしょうか?この記事ではその仕組みを詳しく見ていきます。

ブール演算子 and/or

Pythonのブール演算のand/orは次のようになっています。(公式ドキュメントより)

演算結果注釈
x or yx が偽なら y, そうでなければ x(1)
x and yx が偽なら x, そうでなければ y(2)
not xx が偽なら True, そうでなければ False(3)

注釈:

  1. この演算子は短絡評価されます。つまり第一引数が偽のときにのみ、第二引数が評価されます。
  2. この演算子は短絡評価されます。つまり第一引数が真のときにのみ、第二引数が評価されます。
  3. not は非ブール演算子よりも優先度が低いので、 not a == b は not (a == b) と解釈され、 a == not b は構文エラーです。

それぞれを詳しく見ていきましょう。

短絡評価とは

y = False and X()

のような式があった場合、andの左側がFalseなので、andの右側がTrueであろうがFalseであろうが右辺はFalseと確定します。それゆえ、andの左側がFalseであると確定した時点でandの右側に対しては何も調べる必要がないのです。このような評価の仕方を短絡評価と呼びます。

orについても同様で、

y = True or X()

のような場合、orの左側がTrueであると判明した時点で右辺はTrueと確定します。X()は評価(実行)されません。

ブール演算子 or

x or y の形で用います。

さきほどの説明には、「第一引数が偽のときのみ、第二引数が評価されます」とあります。

つまり、第一引数の真偽を判定せねばならないので、第一引数がtruthyかfalsyか判定するためにbool型への変換が行われます。これは、bool()関数によって行われます → Pythonのtruthyとfalsyについて

bool関数は、オブジェクトである場合、__bool__()メソッドを呼び出します。(これがわからない人は、上のリンク先の記事を確認してください。)

つまり、第一引数がオブジェクトである場合、その__bool__()メソッドが呼び出されます。その返し値がTrueである場合、or全体がTrueであることが確定するので第二引数については評価しません。(何もしません。)

第一引数に対して__bool__()メソッドを呼び出した返し値がFalseである場合、第二引数について調べなければなりません。しかし、このとき、第二引数に関しては、bool()関数を呼び出さずに、単に評価した結果をor全体の値となります。

x = False or 123
print(x)
# 123
// Trueではないことに注意。
// 123がそのまま右辺の値となりxに代入される。

このことは、次のようなコードで確かめられます。

class C:
    def __init__(self, s:str):
        self.s = s

    def __bool__(self) -> bool:
        print(f"__bool__ {self.s}")
        return False

    def __str__(self)->str:
        return str(self.s)

x = C("X")
y = C("Y")

z = x or y
print(z)
# __bool__ X
# Y
// y.__bool__()は呼び出されていない。
// また、zにはyがそのまま代入されていることがわかる。

ブール演算子 and

andに関しても同様で、第一引数をboolに変換し、それがTrueである場合にのみ第二引数を評価します。そのときの値がand全体の値となります。

print(True and 123)
# 123

print(234 and 345)
# 345

短絡評価を用いたデフォルト値の適用

Web系の開発で、str型の値がNoneか””(空の文字列)であれば、デフォルト値を代入させたいことがよくあります。このとき、orの短絡評価を用いて次のように書けます。

s = request.form.get("USER_INPUT") or "DEFAULT"
// get()の返し値がNoneか""であればfalsyなので、
// orの第二引数が評価され、その値がそのままsに代入される。

評価とは?

ここまでに何度か評価と言う用語が出てきましたが、そのまま値を代入しているのとは何が違うのでしょうか?

評価とは、eval()関数だと思うと良いでしょう。インタラクティブモードなどで実行したときの結果と同じです。

つまり、次のような挙動になります。

print(eval("123"))
# 123
// 数値やオブジェクトの場合、それがそのまま値となる

print(eval("1+2"))
# 式の場合、その式の計算結果がその値となる

def f():
    return 234
print(eval("f()"))
# 234
// 関数呼び出しの場合、関数を実行した返し値がその値となる

「評価」と書かれていればeval()と同等の処理と覚えておけば良いですね。

def f():
    return 123

print(True and f()+3)
# 126
// andの第一引数がTrueなので第二引数が評価される。
// f() + 3なので、eval("f()+3")のような処理がなされる。

(Visited 47 times, 1 visits today)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です