search
LoginSignup
83

More than 5 years have passed since last update.

posted at

Python の or と and 演算子の罠

普通、プログラム言語では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): と同じことが勝手におこなわれています。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
83