Pythonでthuthyとfalsyについて

Pythonのifの条件式にはboolean(bool型)を書く必要があります。ところが、一見、booleanではないものが指定された場合どうなるのでしょうか?この記事では、その挙動について詳しく見ていきます。

boolean context

ifの条件式にはbool型を書きますが、bool型ではないものが条件式に指定された場合、これをbool型に変換しなくてはなりません。

このとき、Pythonでは内部的には、bool( )という組み込み関数を通じてbool型に変換されます。(変換しようとします)

bool()関数は、引数で渡されたのがNoneであれば、これをFalseとして扱います。

print(bool(None))
# False

また、bool()関数は、引数で渡されたのがオブジェクトである場合、そのオブジェクトの__bool__()メソッドがあれば、これを呼び出そうとします。

つまり、次の例では、xはFalseとして扱われます。

class X:
    def __bool__(self)->bool:
        return False

x = X()
if x:
    print("Truthy")
else:
    print("Falsy")
# Falsy

thuthyとfalsy

booleanを要求するコンテキストに出現したときにFalseとして扱われる値のことを JavaScriptの用語で、falsy(falseっぽい?)、Trueになる値のことをtruthy(trueっぽい?)と呼びます。

これらは、False/Trueという概念を拡張するときに使われます。さきほどの例であれば、(オブジェクトを指す)変数がNoneであればfalsy、Noneでなければtruthyです。しかし、そのオブジェクトが__bool__()メソッドを持っているなら、このメソッドの返し値によってflasyかtruthyかが決まります。

空のsequenceはFalsy

str(文字列型)やlist型は、sequenceです。Pythonでは空のsequenceはfalsyです。

s = ""
if s:
  print("Truthy")
else:
  print("Falsy")
# Falsy

これはどのような仕組みになっているかと言うと、sequenceは、__len__()メソッドを実装することになっています。つまり、暗黙実装された__bool__()メソッドは__len__()メソッドを呼び出してtruthyかfalsyかを判定しているということです。

  # このような__bool__()が暗黙的に実装されていると考えられる。
  def __bool__(self)->bool:
     return __len__() != 0

__len__()メソッドだけを実装したクラスでこのことを確認しましょう。

class X:
    def __len__(self)->int:
        print("__len__")
        return 1

x = X()
print(bool(x)) # ここで__bool__()メソッドから__len__()が呼び出される
if x: # ここでも__bool__()メソッドから__len__()が呼び出される。
    print("Truthy")
else:
    print("Falsy")
# __len__
# True
# __len__
# Thuthy

確かに__len__()メソッドが呼び出されています。

sequenceがNoneか空である判定

Noneもfalsyなので、C#などでよくある、nullか””(空の文字列)であるかの判定である、if ( ! s.IsNullOrEmpty() ) は、単に if s : と書けます。

s = None
print(bool(s))
# False

s = ""
print(bool(s))
# False

s = "ABC"
print(bool(s))

ユーザー定義型がtruthyであるかfalsyであるかの判定

以上のことから、ユーザー定義型についてそれがtruthyであるか、falsyであるかは次の組み合わせがあることがわかりました。

オブジェクト変数がNoneである→falsy

オブジェクト変数がNoneではないなら以下の分類になります。

__bool____len__ truthy or falsy ?
定義されていない 定義されていない truthy
定義されていない定義されている__len__()が非0を返したときtruthyさもなくばfalsy
定義されている どちらでも __bool__()の返し値そのまま。
※ __len__()が呼び出されるかどうかは__bool__()の実装次第。
(Visited 49 times, 1 visits today)

コメントを残す

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