Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
72
Help us understand the problem. What is going on with this article?

More than 5 years have passed since last update.

@keisuke-nakata

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): と同じことが勝手におこなわれています。

72
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
72
Help us understand the problem. What is going on with this article?