0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python: if x == True で怒られた理由と x, x == True, x is True の違い

Posted at

背景

Pythonでちょっとしたツールを作ってて、テストを書いてたら Github Actionsで設定していたRuff (Pythonの静的解析ツール)にこう怒られました。

E712: Comparison to True should be 'if cond:' or 'if not cond:'

(この E712flake8(のベースになっている pycodestyle)に由来するルール)

これが、Pythonにおける、x, x == True, x is True の違いについて考えるきっかけになったので、そのメモを残します。


なにがダメなの?

Ruffが怒ってるのは、比較演算子 == True が冗長で非推奨っていう理由です。

具体的には、

if hoge == True

は嫌で

if hoge

がいいっていう話。

うーん でも

if hoge == True

「明示的で良いのでは?」 そこまで怒る必要あるんかな
(Javaとか、SQLの==TRUEとか, Cの==1イメージ)


なぜ == True がまずいのか?

1. 冗長でPythonicじゃない

PEP8(Pythonのスタイルガイド)https://peps.python.org/pep-0008/ でも、

Don't compare boolean values to True or False using ==.

つまり
Python では「真偽値の判定」はそのまま書くのが自然。

# Correct:
if is_active:

# Wrong:
if is_active == True:

# Worse:
if is_active is True:

Pythonらしくないってだけなら「それはそうか」な話...


どう書くのが正解か?

改めてPEP8

Don't compare boolean values to True or False using ==.

Yes: if greeting:
No: if greeting == True:
Worse: if greeting is True:

ほんまに?

PEP 8ではis Trueはさらに悪い書き方だとされてるけど
実際にはTrue以外の「Truthy(真と評価される値)」な値(例: 1や空でない文字列)と、純粋なTrueを区別したい場面も存在するので

その辺も踏まえてまとめると

  • Truthy:1 や "abc" のように True として扱われる値
  • Falsy:0 や None のように False として扱われる値)
判定の種類 書き方 推奨度
Truthyか? if x: / assert x ⭐️⭐️⭐️⭐️⭐️
Falsyか? if not x: / assert not x ⭐️⭐️⭐️⭐️⭐️
厳密にTrueか? if x is True: / assert x is True ⭐️⭐️⭐️(必要なら)
Trueと等しいか? if x == True: / assert x == True ⭐️(避けるべき)

うーん PEP8とは少し評価が違うが、観点が違うということで。
(⭐️は筆者の主観(可読性・バグリスク基準))

補足①
is Trueは、Trueかもしれないし、Noneや他の型の値も返ってくる可能性がある」 といった、関数の戻り値の型が複数ありえるケースで、厳密な型チェックをしたい場合に有効な手段だと考えて⭐️3つ

補足② 真偽値表 
3つとも振る舞いが違う
== Trueの振る舞いは boolがintのサブクラスであること(PEP285)を押さえておけばよさそう

== True の結果 is True の結果 Truthy? (if x) 理由
1 ✅ True ❌ False ✅ True boolint のサブクラス。1 == True は True だが同一オブジェクトではない。
"yes" ❌ False ❌ False ✅ True 空でない文字列は Truthy。等価比較は型が違うので False。
[1] ❌ False ❌ False ✅ True 空でないリストは Truthy。list == True は False。
True ✅ True ✅ True ✅ True 真偽値そのもの。
1.0 ✅ True ❌ False ✅ True 1.0 == 1 == True が成り立つ(数値比較連鎖)。同一性は成立しない。
0 ❌ False ❌ False ❌ False 0 は Falsy。0 == True は False。0 == False は True。
None ❌ False ❌ False ❌ False None は Falsy。
False ❌ False ❌ False ❌ False False == True は False。False is True も False。
【補足】
(1) `"yes" == True` が False なのは、より厳密には、左辺の `str.__eq__` も右辺(`bool` は `int` のサブクラス)側の `int.__eq__` も互いを等価と判定しないため。

結局、JAVAやCと違って任意のオブジェクトに真偽値を適用できるので、こういう感じになってますね、おそらく


おまけ

注意として、__eq____bool__がオーバーロードされているユーザー定義クラスのインスタンスは、振る舞いが変わってきます。(例えばnumpyのndarray, PandasのSeries)

import numpy as np
a = np.array([True, False, True])
result_True = a == True
result_False = a == False
print(result_True)  # → [ True False  True ]
print(result_False)  # → [ False  True False ]
print(type(result_True))  # → <class 'numpy.ndarray'>
print(type(result_False))  # → <class 'numpy.ndarray'>

つまり a == Truebool じゃなくて ndarray(bool) が返ってきます。

numpyにおいては意図に応じて明示的に書く必要があります:

意図 書き方
すべての要素が True か? assert a.all()
いずれか1つでも True か? assert a.any()

assert aif a: のように書いても、同じValueErrorが発生します。
(この挙動は、pandasSeriesなど、他のライブラリでも共通しています。「暗黙の真偽値評価」ができないのです。)

このへんが Python のリストや普通の変数と違ってややこしいところ。

まとめ

  • assert x == True は Python では非推奨(E712)
  • 真偽値の比較は assert x / assert not x が基本
  • is True を使いたいときは「型まで厳密に見る」場面だけ
  • ユーザー定義クラスのインスタンスは、boolの振る舞いの仕様が違うことがあるよ

参考リンク

PEP 285 を読むと「なぜ boolint のサブクラスとして導入したか」「if x == True を仕様側で特別扱いしなかったか」という経緯が書かれていて結構面白いです。

0
0
0

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
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?