sec03 - 【完全ガイド】Pythonのbool型とTrue/Falseの仕組み:比較・論理演算子の使い方まで徹底解説
スポンサーリンク

Pythonのbool型とは?TrueとFalseの基本

これまで、True, Falseをif文などで使用してきましたが、このレクチャーでは、それらの仕組みをもう少し掘り下げて解説します。

if文のように条件によって処理を分岐させるものは、その条件が正しい(真/True)か正しくない(偽/False)かによって、次の処理の流れが決まります。

bool型の値は、True, Falseの2つのみです。

# bool
True
False

Pythonの比較演算子まとめ(==, !=, <, >, <=, >=)

Pythonの比較演算子の意味と使い方

  • a == b
    • aとbの値が同じかどうかを調べます: 同じならばTrue
    • =は値の代入、==は両辺の値の比較になります
      • 初心者が特に間違えやすいポイントです
    • 数値の場合は、intfloatとで型が違っても、同じ値とみなされればTrueになります
      • 1 == 1.0Trueになります
      • ただし、1 == '1' のように数値と文字列を比較する場合はFalseになります
    • 文字列の場合、文字の生成手順によってid()の結果が変わる場合がありますが、文字の並びが同じであればTrueになります
      • 文字列は、大文字と小文字を区別します
str1 = 'ABC'
str2 = f"A{'B'}C"
print(str1, id(str1))  # 'ABC' 2265151374416 (idは違う)
print(str2, id(str2))  # 'ABC' 2265158076096 (idは違う)
print(str1 == str2)    # 文字の並びが同じなので、True
  • a != b
    • 両辺の値が異なるかどうかを調べます: 違えばTrue
    • 文字列は、1文字でも違えばTrueになります
      • 'apple' != 'Apple'True(大文字小文字を区別)
  • a < b
    • aがbより小さいかどうかを調べます: aがbより小さければTrue
    • 大小比較の演算子では、int/floatとstrの比較はできず、TypeErrorになります
      • これは、a <= b, a > b, a >= bの場合も同様です
    • 数値では、数の大小で比較します
      • 3 < 5True
      • 5 < 3False
    • 文字列でも比較はできますが、文字コード(Unicode)で割り振られている順序で判断されます
      • 'a' < 'b'True('a'の方がUnicode上の値は小さい)
      • 'A' < 'a'True(大文字の方が先 (割り振られている番号が小さい))
    • 文字列の比較は、辞書の索引順に近い動きをする点に注意しましょう
      • 'apple' < 'banana'True
      • 'apple' < 'Apple'False
        • ('A' の Unicode 値は 'a' より小さいため、'Apple' の方が「辞書順で前」に来ます。
          したがって 'apple' < 'Apple'False になります。)
  • a <= b
    • aがbより小さいまたは等しいかを調べます: aがb以下ならTrue
    • <== の両方を含んだ条件です
      • 3 <= 5True
      • 5 <= 5True
    • 文字列の場合も同様に、文字コード順で比較されます
  • a > b
    • aがbより大きいかどうかを調べます: aがbより大きければTrue
    • < の逆の関係になります
      • 7 > 3True
      • 3 > 7False
    • 文字列の場合は、Unicode順で判断されます
      • 'z' > 'a'True
      • 'A' > 'a'False
  • a >= b
    • aがbより大きいまたは等しいかを調べます: aがb以上ならTrue
    • >== の両方を含んだ条件です
      • 5 >= 5True
      • 10 >= 2True
    • 文字列の比較でも、同様にUnicode順を基準にします
  • a < x < b
    • 0 < num <=10 のように、変数の値が一定の範囲内に収まっているかどうかを調べることもできます
    • <, >, <=, >=(大小比較)の演算子を使うことができますが、通常は(コードの読みやすさからも)、<<=の組み合わせで構成することになるでしょう

Pythonで比較可能なデータ型と不可能な例

比較できるもの、できないもの

等価比較 (== / !=)

異なる型でも比較は可能(ただし、意味のある結果になるとは限りません)。Pythonは、両辺の値が「等しいかどうか」を判定しようとしますが、型が違えば普通はFalseになります。


大小比較(<, >, <=, >=)

異なる型では比較できません(Python 3ではエラーになります)。特に、数値と文字列の大小比較は「意味がない」と判断され、TypeErrorが発生します。

同じ型同士であれば比較可能です:

  • 数値同士(int, float, Decimal など)
  • 文字列同士(Unicode順で比較)

演算子型が違っても比較できるか例 (1 vs '1')結果またはエラー
==できる(Falseになる)1 == '1'False
!=できる(Trueになる)1 != '1'True
<できない(エラー)1 < '1'TypeError
<=できない(エラー)1 <= '1'TypeError
>できない(エラー)1 > '1'TypeError
>=できない(エラー)1 >= '1'TypeError
スポンサーリンク

Pythonの真理値(Truth Value)の判定方法

PythonのTrueとFalseは整数としても使える

まず、次のコードを見てください。

int(True)   # 1
int(False)  # 0
True == 1   # True
False == 0  # True

Pythonでは、TrueFalseを整数に変換すると、True1False0として扱われます。つまり、整数値の10はboolとしても扱われます。

たとえば次のようなコードを書いても動きます。if 1:Trueに、if 0:Falseになります。

# True
if 1:
    print('True')
else:
    print('False')

# False
if 0:
    print('True')
else:
    print('False')

Pythonのすべてのオブジェクトの真偽値判定

Pythonでは、どのオブジェクトも、True / False を判定できます。数値だけでなく、文字列やlist、dictなどでも真偽を判定できます。次のコードでは、変数greetingの値('Hello')は「空でない文字列」なので、Trueと同じように扱われます。したがって「Trueと判定される」と表示されます。

greeting = 'Hello'
if greeting:
    print('Trueと判定される')

PythonでFalseと同じように扱われる値一覧

まずは、Falseと同じように振る舞う値を覚えましょう。なぜなら、Falseとして扱われるパターンは少ないからです。

Falseのように振る舞うデータには、「値がゼロ」「中身が空」「存在しない(None)」という共通点があります。

# False
0
0.0
''       # 空文字
[]       # 要素を持たないlist
{}       # 要素を持たないdict
tuple()  # 要素を持たないtuple
set()    # 要素を持たないset
None     # None (何も存在しないことを明示的に表すデータ型)

PythonでTrueと同じように扱われる値一覧

上記以外の値はすべてTrueとして扱われます。「空でなければTrue」、中身があることがポイントです。

つまり、listなどのデータ構造の場合は、要素(中身)が存在していれば、値が0NoneであってもTrueになります。重要なのは「空でないこと」です。

# True
1
-1
0.0001
-0.0001
'0'     # どんな文字であっても、1文字以上の文字列
' '     # 空白も、True
[None]  # 要素の中身が 0 や None、空文字でも、list自体が空でなければ True  (以下の例も同様に、要素が存在すればTrue)
{0: None}
(0,)
{0}

具体的な使いどころとして、たとえば「listの要素が存在していればpop()する」といった場面を考えてみましょう。pop()は要素が存在しない時はエラーを発生させるため、安定的にプログラムを実行させるために、事前に要素の有無を確認しておくことが重要です。

if len(num_list) > 0:のように、『要素数が0より大きければ処理を行う』というような書き方もできますが、それよりもif num_list:のように記述した方がシンプルで可読性も向上します。この時、変数num_listの要素数が0だった場合はif文のブロックは処理されず、要素が1つ以上存在している時のみ処理されます。

num_list = [1, 2, 3]

# listの要素が存在していれば、popする
if num_list:
    result = num_list.pop()
    print(num_list)
    print(result)

Pythonのnot演算子の使い方

notは、条件の「反対」を表す演算子です。TrueFalseに、FalseTrueに変えます。

a = True
print(not a)   # False

b = False
print(not b)   # True

not を使うと、条件を反転させて「〜でなければ」という形にできます。例えば、次のコードは、『listの要素がなければ、要素を追加する』という処理を行っています。

num_list = []

# listに要素が無ければ、要素を追加する
if not num_list:
    num_list.append(None)
    print(num_list)

Pythonのin/not in演算子で要素の有無を判定

a in b, a not in b のように記述し、abの中に「含まれている(in)」か「含まれていない(not in)」かを調べます。

Pythonで文字列のin/not in判定方法

文字列では、部分文字列として含まれているかを判定します。これは、大文字と小文字を区別します。

例えば、'can' in 'I can do it.'というコードは、'can'の文字の並びが'I can do it.'の文字列の中に含まれているかどうかを調べ、含まれていればTrueになります。

text = 'I can do it.'
print('can' in text)  # True ('can'は含まれる)
print("can't" not in text)  # True ("can't"の文字の並びは含まれないのでTrue)

Pythonでlist, tuple, set内の要素確認

list, tuple, setの場合は、要素の中に値が存在するかを判定します。

num_list = [1, 2, 3]
print(1 in num_list)      # True
print(4 in num_list)      # False
print(1 not in num_list)  # False
print(4 not in num_list)  # True

Pythonでdict内のkey・valueの存在確認

dictでは、keyの存在を調べます(値は対象になりません)。ただし、dict.values()valueを取得した後で、値が含まれているかどうかを調べることはできます。

# keyの有無を調べる
tmp_dict = {'key1':100, 'key2':200}
print('key1' in tmp_dict)      # True
print(100 in tmp_dict)         # False (100というkeyは存在しない)
print('key1' not in tmp_dict)  # False ('key1'のkeyは存在するので、[not in]はFalseになる)
print(100 not in tmp_dict)     # True  (100というkeyは存在しないのでTrue)

# valueの有無を調べる
tmp_dict = {'key1':100, 'key2':200}
print(100 in tmp_dict.values())      # True (valueに100が存在する)
print(500 not in tmp_dict.values())  # True (500というvalueは存在しない)

Pythonのis/is not演算子の使い方

isは「同じデータ(オブジェクト)を参照しているかどうか」を確認します。==の「値が同じかどうか」を比較する点と異なります。

同じ値でも異なるオブジェクトの判定

次のコードの、変数abは見た目の値は同じ('ABC')ですが、id()を調べてみると、違う場所に保存されたデータを参照していることがわかります。

この時、==Trueになりますが、isFalseになります。ですので、"値"を調べたいのか、"参照先"を調べたいのかで、==isを使い分けます。

a = 'ABC'
b = f"{'A'}{'B'}{'C'}"

id(a)  # 例: 139982392668688
id(b)  # 例: 139979347634776

a == b   # True   値が同じだから
a is b   # False  参照先(オブジェクト)が違うから

同じオブジェクトを参照している場合

b = aのように、同じオブジェクトを参照させるようにすると、isTrueを返します。

b = a

id(a)  # 例: 139982392668688
id(b)  # 例: 139982392668688 (aと同じ)

a == b   # True   値が同じ
a is b   # True   同じデータを参照している

Pythonのis not演算子の使い方

is notは、「同じオブジェクトではない」という意味になります。

x = [1, 2, 3]  # id例: 2066161843712 (異なるid)
y = [1, 2, 3]  # id例: 2066161828672 (異なるid)

x == y     # True   値は同じ
x is y     # False  別のリストを参照
x is not y # True   同じオブジェクトではないのでTrue

Pythonのand / or論理演算子の使い方

andの動作と評価順序

andは、『両方の条件がTrueの場合のみ』、Trueになる論理演算子です。

例えば、次のようなコードを考えます。

num = 5
if num > 0:
    if (num % 2) == 1:  # [num % 2]の結果(除算の余り)が"1"なら奇数である
        print('正の数 & 奇数')  # num=5 の場合、出力されます

上記コードは、変数numの値が正の数で、かつ奇数の場合にprint('正の数 & 奇数')が実行されます。これと同じ処理をandを使って1行で書くと、次のようになります。

num = 5
if (num > 0) and ((num % 2) == 1):
    print('正の数 & 奇数')  # num=5 の場合、出力されます
評価の順番

上記コードのように、各条件式を丸カッコで囲わなくても、条件の評価は問題なく動作します。Pythonでは演算子の優先順位が決められており、上位の演算子から優先的に評価が行われるため、if num > 0 and num % 2 == 1:のように記述しても、意図した通りに判定されます。

しかし、丸カッコで囲っておくと、どの条件とどの条件をandで演算しているのかが分かりやすくなるので、可読性向上のために筆者は丸カッコを使ってコードを書くようにしています。

※ 当然ながら、Pythonのデフォルトの優先順位と異なる順に計算(評価)を行いたい場合は、丸カッコで優先順位をコントロールする必要があります。

演算子説明
(expressions...),[expressions...],
{key: value...}{expressions...}
結合式または括弧式、リスト表示、辞書表示、集合表示
x[index]x[index:index],
x(arguments...)x.attribute
添字指定、スライス操作、呼び出し、属性参照
await xAwait 式
**べき乗 [5]
+x-x~x正数、負数、ビット単位 NOT
*@///%乗算、行列乗算、除算、切り捨て除算、剰余 [6]
+-加算および減算
<<>>シフト演算
&ビット単位 AND
^ビット単位 XOR
|ビット単位 OR
innot inisis not,
<<=>>=!===
所属や同一性のテストを含む比較
not xブール演算 NOT
andブール演算 AND
orブール演算 OR
if -- else条件式
lambdaラムダ式
:=代入式

andの評価は次のように行われます。

  • 左側の条件がFalseの場合、右側の条件は確認されずに結果はFalseになります
  • 両方Trueの場合のみTrueとなります
print(True and True)   # True  (両方の条件が確認される)
print(True and False)  # False (両方の条件が確認される)
print(False and True)  # False (左側の条件がFalseなので、右側の条件は確認されない)
print(False and False) # False (左側の条件がFalseなので、右側の条件は確認されない)

orの動作と評価順序

orどちらか一方がTrueならTrueになります。両方Falseの場合のみFalseです。

  • 左側がTrueの場合、右側は確認されずTrueが返されます
  • 左側がFalseの場合、右側の条件を確認してTrueFalseを返します
print(True or True)    # True  (左側の条件がTrueなので、右側の条件は確認されない)
print(True or False)   # True  (左側の条件がTrueなので、右側の条件は確認されない)
print(False or True)   # True  (両方の条件が確認される)
print(False or False)  # False (両方の条件が確認される)

and と or の組み合わせ例

例として、スポーツクラブを利用できる条件を考えます。

  • 会員である または
  • 18歳以上 かつ 1日利用料金を支払済み

この条件をPythonで書くと次のようになります。

# 利用者情報
person = {
    'member': True,
    'age': 18,
    'paid': True
}

# 利用可能か判定
if person['member'] or (person['age'] >= 18 and person['paid']):
    print('スポーツクラブを利用できます')
else:
    print('利用できません')
  • memberTrueの場合、右側の条件は確認されずに利用可能になります
  • memberFalseの場合、右側の条件 (age >= 18 and paid) が評価されます

andorが混在する場合、丸カッコを使ってコードを整理すると、条件の評価順が分かりやすくなります。

なお、条件が長くなる場合は、改行して記述できます。改行させながら記述したい時は、条件全体を丸カッコで囲います。

(if a or b and c: -> if (a or b and c):)

if (person['member'] or
        (person['age'] >= 18 and person['paid'])):
    print('スポーツクラブを利用できます')

このように、条件全体を丸カッコで囲うことで、改行して条件を書いても正しく評価されます。また、可読性も向上します。

なお、改行した条件の部分(上記コードの2行目)のインデントについては、厳密なルールはなく、スペース4つでも構文としては問題ありません。しかし、スペース4つにすると、ブロック内のコードと見分けが付きにくくなってしまいます。

if (person['member'] or
    (person['age'] >= 18 and person['paid'])):  # この行と次の行のインデントが同じ
    print('スポーツクラブを利用できます')

そのため、PythonのPEP 8(Code Lay-out)では、スペース8つ(インデント2個分)にすると可読性が向上するとされています。(もしくは、ブロックの先頭にコメントを付けることで条件文とブロックを隔てることができるとしています。)

if (person['member'] or
        (person['age'] >= 18 and person['paid'])):  # インデント2つ分にする
    print('スポーツクラブを利用できます')

if (person['member'] or
    (person['age'] >= 18 and person['paid'])): 
    # コメント(ブロックに関する説明文など)を入れて、条件文とブロックを隔てる
    print('スポーツクラブを利用できます')
スポンサーリンク