普通、プログラム言語ではor
とand
(または||
と&&
などの類似表現) は (2項の) 論理演算を意味します。
つまりor
やand
が返す値は bool 値 (True/False) です。
C系の言語では True/False が 1/0 で代用されたりもしますが、とにかく論理演算 or と and は論理値に相当する値を返します。
しかし、Python では、or
とand
は論理値を返すとは限りません。
Pythonにおいて、式x and y
は、次と等価です:
if not bool(x): # xの論理値がFalseなら
return x # yを見ることなく、しかもFalseではなくxそのものをリターン
else: # xの論理値がTrueなら
return y # bool(y) ではなく、yそのものをリターン
同様に、式x or y
は次と等価です:
if bool(x): # xの論理値がTrueなら
return x # yを見ることなく、しかもTrueではなくxそのものをリターン
else: # xの論理値がFalseなら
return y # bool(y) ではなく、yそのものをリターン
Pythonでは、長さ0の文字列の論理値がFalse
、長さ1以上の文字列の論理値がTrue
であることに気をつけると、次のようにまとめることができます:
'123' or '456' -> '123' # bool('123') がTrueなので、 左辺値'123'をリターン
'123' or '' -> '123' # bool('123') がTrueなので、 左辺値'123'をリターン
'' or '456' -> '456' # bool('') がFalseなので、右辺値'456'をリターン
'' or '' -> '' # bool('') がFalseなので、右辺値'' をリターン
'123' and '456' -> '456' # bool('123') がTrueなので、 右辺値'456'をリターン
'123' and '' -> '' # bool('123') がTrueなので、 右辺値'' をリターン
'' and '456' -> '' # bool('') がFalseなので、左辺値'' をリターン
'' and '' -> '' # bool('') がFalseなので、左辺値'' をリターン
上の例では、or
とand
の結果であるにも関わらず、True/False
は一切返ってきません。
もしもPythonで2項論理演算子 (orとand) から返ってくる値が常に論理値True/False
であることを保証したい場合は、bool(x) or bool(y)
またはbool(x) and bool(y)
とする必要があります (bool(x or y)
, bool(x and y)
でも良いですが)。
普通、x
やy
として論理値True/False
以外の値を渡すことはないですが、自動的にbool(x)
が評価されるため、たまに忘れることがあります。
なんでこんなことになっているのかというと、よく言われているのは、None
の扱いが便利になるからだそうです。
PythonではNone
の論理値はFalse
です (bool(None) -> False
) ので、次のコード
# x には None が格納されているのかどうかわからない
if x is None:
x = '456'
# x = '456' if x is None とも書けるが個人的に好きじゃない
は、x = x or '456'
で代用できます。
ただし、等価ではありません。x
に 空文字列''
や 空リスト[]
が格納されていた時の振る舞いが異なります。
x
に''
や[]
が格納されていた場合、x is None
もbool(x)
もFalse
です。よって、 is None
を用いたコードの場合x
は再代入されませんが、
x or '456'
を用いたコードの場合x
には'456'
が再代入されます。
待て、 if x or y:
はどうなる?
もしもor
が論理値を返さないとするならば、if
で今までx or y
を使っていたのは偶然うまくいっていたのか?
いえ、それは偶然ではなく必然的にうまくいっていました。
Pythonでは、if
の条件式は勝手に真理値が判別されます。つまり、if bool(x or y):
と同じことが勝手におこなわれています。