Python の or と and 演算子の罠

  • 29
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

普通、プログラム言語ではorand (または||&&などの類似表現) は (2項の) 論理演算を意味します。
つまりorandが返す値は bool 値 (True/False) です。
C系の言語では True/False が 1/0 で代用されたりもしますが、とにかく論理演算 or と and は論理値に相当する値を返します。
しかし、Python では、orandは論理値を返すとは限りません。
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なので、左辺値''   をリターン

上の例では、orandの結果であるにも関わらず、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) でも良いですが)。
普通、xyとして論理値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 Nonebool(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): と同じことが勝手におこなわれています。