SIGMA-SE Math & Tech Library

SIGMA-SE Math & Tech Library


数学と情報技術をテーマに、書籍や教材だけではつかみにくい考え方を具体例とともに簡潔にわかりやすく伝える解説サイトです。
技術の歴史や背景、関連知識の整理、学習のための覚書や要約記事も掲載しています。

Python - 論理演算子:or・and・notと真偽値判定

概要

Pythonの論理演算子or、and、notを真理値判定と実行順序から整理する。

再試行回数の0を残す初期値設定と、ゼロ除算を避ける条件式を比較する。何が偽になるかに加えて、どの値が返り、どこまで式が実行されるかを確認する。設定値と短絡評価の比較例はPython 3.12.2で確認。

この記事の構成

各論理演算子の使い方と実装サンプル

論理演算子の種類

Pythonではbool型以外のオブジェクトも条件式に使える。組込み型では数値のゼロ、空の文字列やコンテナ、NoneなどがFalseとなり、それ以外は原則としてTrueとなる。独自クラスでは__bool__()または__len__()で判定方法を定義できる。

True/Falseの判定基準

論理演算で最も重要となるTrue/Falseの判定基準として、空文字や空リスト等もFalseと判定される。
下記は偽と判定される代表例である。独自クラスなどでは型が定めた真理値判定に従い、判定自体が例外になる場合もある。

  • False判定一覧
    False判定となる要素 説明
    False bool型のFalse
    None 何もないことを示すオブジェクト(≒多言語のNull)
    0 int型(整数)のゼロ
    0.0 float型(浮動小数点数)のゼロ
    0j complex型(複素数)のゼロ
    Decimal(0) decimal型のゼロ
    Fraction(0, 1) fraction型(有理数)のゼロ
    '' str型(文字列)の空文字
    [] list型(配列)の空配列
    {} dict型(連想配列)の空配列
    () tuple型(タプル)の空配列
    set() set型(集合)の空配列
    range(0) range型(数値配列)の空配列

※ 各実装サンプルは、下記ページを参考。

以降、論理演算子に関する実装サンプルを対話モード(インタプリタ)で解説する。

論理和(or)

a or bは、前方から評価して最初にTrueとなるオペランドを返す。そこで評価を終える動作をショートサーキットという。
a、b共にFalseである場合は、末尾の要素bを返す。

  • 論理和パターン

    a の評価 b の評価 a or b の戻り値
    True False a の値
    False True b の値
    True True a の値
    False False b の値
  • 論理和 実装サンプル(論理和パターン)

    $ python
        >>> # True or False (int型 or int型)
        >>> bool_a = 1 or 0
        >>> print(bool_a)
        1
        >>> # False or True (float型 or float型)
        >>> bool_b = 0.0 or 1.0
        >>> print(bool_b)
        1.0
        >>> # True or True (complex型 or complex型)
        >>> bool_c = 1j or 2j
        >>> print(bool_c)
        1j
        >>> # False or False (list型 or dict型)
        >>> bool_d = [] or {}
        >>> print(bool_d)
        {}
        >>>
    
  • ショートサーキットの例
    複数の候補は左から評価され、最初に真となった値が返る。順番を変えると返る値や実行される処理も変わるため、候補の優先順位に合わせて並べる。

    $ python
        >>> # 先頭の 2 (True) のみで評価が返される。
        >>> # 以降の 0 (False)、1 (True) は評価しない。
        >>> bool_a = 2 or 0 or 1
        >>> print(bool_a)
        2
        >>> # 2項目の 4 (True) で評価が返される。
        >>> # 以降の 3 (True)、2 (True)、1 (True) は評価しない。
        >>> bool_b = 0 or 4 or 3 or 2 or 1
        >>> print(bool_b)
        4
        >>>
    
  • 有効な0を初期値で置き換えない
    再試行回数は0なら再試行なし、Noneなら未設定として扱う。未設定のときだけ3回へ補うつもりでorを使うと、0回の指定も置き換わる。

    for retries in (None, 0, 5):
        by_or = retries or 3
        by_none = 3 if retries is None else retries
        print(retries, by_or, by_none)
    
    None 3 3
    0 3 0
    5 5 5
    

    各行は「元の指定・orの結果・Noneだけを補う結果」の順である。orは未設定かを調べる演算子ではなく、左辺が偽なら右辺を返す演算子なので0も対象になる。

    空文字列や0をすべて既定値へ置き換えたい仕様ならorを使える。値として有効な0を残したい場合はis Noneで未設定を判定する。入力の型や範囲の検査は別途必要であり、この例は非負整数またはNoneを前提とする。

論理積(and)

a and bは、前方から評価していきFalseとなる要素が見つかった時点(ショートサーキット)でその要素を返す。 a、b共にTrueである場合は、末尾の要素bを返す。

  • 論理積パターン

    a の評価 b の評価 a and b の戻り値
    True False b の値
    False True a の値
    True True b の値
    False False a の値
  • 論理積 実装サンプル(論理積パターン)

    $ python
        >>> # True and False (int型 and int型)
        >>> bool_a = 1 and 0
        >>> print(bool_a)
        0
        >>> # False and True (float型 and float型)
        >>> bool_b = 0.0 and 1.0
        >>> print(bool_b)
        0.0
        >>> # True and True (complex型 and complex型)
        >>> bool_c = 1j and 2j
        >>> print(bool_c)
        2j
        >>> # False and False (list型 and dict型)
        >>> bool_d = [] and {}
        >>> print(bool_d)
        []
        >>>
    
  • ショートサーキットの例 左から評価して最初に偽となった値を返し、残りの式は実行しない。後ろの式を実行できる条件を先に確認すると、前提を満たさない計算を避けられる。

    $ python
        >>> # 先頭の 0 (False) のみで評価が返される。
        >>> # 以降の 1 (True)、2 (True) は評価しない。
        >>> bool_a = 0 and 1 and 2
        >>> print(bool_a)
        0
        >>> # 3項目の 0 (False) で評価が返される。
        >>> # 以降の 4 (True)、5 (True) は評価しない。
        >>> bool_b = 1 and 2 and 0 and 4 and 5
        >>> print(bool_b)
        0
        >>>
    
  • ゼロ除算を避ける条件は左側で確認する
    「分母が0でなく、割った結果が2を超える」という条件を考える。関数内で表示させ、割り算が実行されたかも確認する。

    def exceeds_two(numerator, denominator):
        print("divide", numerator, denominator)
        return numerator / denominator > 2
    
    
    for denominator in (0, 2):
        result = denominator != 0 and exceeds_two(6, denominator)
        print("result", denominator, result)
    
    result 0 False
    divide 6 2
    result 2 True
    

    分母が0のときは左辺が偽なので関数自体が呼ばれない。次のように逆順にすると先に割り算が実行され、右側の判定では防げない。

    denominator = 0
    try:
        print(6 / denominator > 2 and denominator != 0)
    except ZeroDivisionError as error:
        print(type(error).__name__)
    
    ZeroDivisionError
    

    真になりやすさや偽になりやすさだけで順番を変えず、後続の式が安全に評価できる前提と副作用を確認する。長い条件は名前の付いた変数やif文へ分けると、その依存関係を追いやすい。

論理否定(not)

not xは、対象xを否定した結果をbool型で返す。
FalseならTrueを返し、TrueならFalseを返す。

  • 論理否定 実装サンプル
    $ python
        >>> # int型 0 の否定
        >>> bool_a = not 0
        >>> print(bool_a)
        True
        >>> # int型 1 の否定
        >>> bool_b = not 1
        >>> print(bool_b)
        False
        >>> # float型 0.0 の否定
        >>> bool_c = not 0.0
        >>> print(bool_c)
        True
        >>> # float型 2.5 の否定
        >>> bool_d = not 2.5
        >>> print(bool_d)
        False
        >>> # complex型 0j の否定
        >>> bool_e = not 0j
        >>> print(bool_e)
        True
        >>> # complex型 1j の否定
        >>> bool_f = not 1j
        >>> print(bool_f)
        False
        >>> # int型 1 and int型 2 and int型 0 の否定
        >>> bool_g = not (1 and 2 and 0)
        >>> print(bool_g)
        True
        >>>
    

まとめ

  • orとandはTrue/Falseへ変換した値ではなく、選ばれたオペランドの値を返す。notはboolを返す。
  • 0もNoneも真理値判定では偽になる。有効な0を残して未設定だけを補う場合はis Noneを使う。
  • 短絡評価では後ろの式が実行されないことがある。後続の計算に必要な前提を左側で確認する。
  • 条件の並べ替えは返る値や副作用にも影響する。処理の意味を保てるか確認してから変更する。

参考文献

この記事を共有
Xで共有 Facebookで共有 LINEで共有



Copyright SIGMA-SE All Rights Reserved.
s-hama@sigma-se.jp