2
1

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 == None: と書いてはいけない理由を徹底的に説明する

Last updated at Posted at 2025-06-20

はじめに

Pythonを学んでいると、「Noneの判定に==を使ってはいけない、isを使いなさい」というアドバイスをよく見かけます。しかし、その理由を調べてみると、多くの記事では「isを使いましょう」という結論だけで、なぜそうすべきなのか、その背景にある仕組みまで踏み込んで解説しているものは少ないように感じました。

また、情報が断片的であったり、信頼できる出典が示されていなかったりすることも少なくありません。結局、正確な情報を得るためには、複数の記事を読み比べ、公式ドキュメントまで遡る必要がありました。

そこでこの記事では、以下の点について、公式ドキュメントなどの信頼できる情報を元に、包括的(self-contained)に解説することを目指します。

  • なぜNoneの判定に==ではなくisを使うべきなのか、その根本的な理由
  • if x is None vs. if not x(暗黙的な偽)の使い分け
  • is演算子を使う上での思わぬ落とし穴

この記事が、皆さんのPythonコーディングにおける迷いを一つでも解消できれば幸いです。

結論:isは"同一性"、==は"等価性"を比べる

早速結論から述べます。Noneの判定にisを使うべき理由は、isがオブジェクトの「同一性(identity)」を比較するのに対し、==はオブジェクトの「等価性(equality)」を比較するからです。

そして、NoneはPythonの実行環境全体でただ一つしか存在しない「シングルトン(Singleton)」オブジェクトであるため、同一性での比較が最も確実なのです。
実際、公式ドキュメントでは次のように説明されています。

This type has a single value. There is a single object with this value. This object is accessed through the built-in name None. It is used to signify the absence of a value in many situations, e.g., it is returned from functions that don’t explicitly return anything. Its truth value is false.

(この型は単一の値を持ちます。この値を持つオブジェクトは一つだけです。このオブジェクトは組み込み名Noneを通じてアクセスされます。多くの状況で値が存在しないことを示すために使用されます。例えば、明示的に何も返さない関数からはNoneが返されます。その真偽値はFalseです。)

それぞれ詳しく見ていきましょう。

is演算子:オブジェクトの"同一性"を比較する (id())

is演算子は、2つの変数が全く同じオブジェクトを指しているかどうかを判定します。これは、言い換えるとオブジェクトのメモリ上のアドレスが同じかどうかを調べている、ということです。実際、Pythonには、オブジェクトのメモリアドレスを返すid()という組み込み関数があり、a is bという式は、内部的にはid(a) == id(b)を実行しているのと等価です。

# is は id() が一致するかを見ている
a = [1, 2, 3]
b = a          # bはaと全く同じオブジェクトを指す
c = [1, 2, 3]  # cはaと同じ"値"を持つが、別のオブジェクト

print(f"a is b: {a is b}")  # aとbは同じオブジェクトなのでTrue
print(f"id(a) == id(b): {id(a) == id(b)}")

print(f"a is c: {a is c}")  # aとcは違うオブジェクトなのでFalse
print(f"id(a) == id(c): {id(a) == id(c)}")

実行結果:

a is b: True
id(a) == id(b): True
a is c: False
id(a) == id(c): False

ちなみに、公式ドキュメントでは、is演算子は次のようにも説明されています。つまり、デフォルト動作では、x is yTrueならばx == yTrueになるということです。

The default behavior for equality comparison (== and !=) is based on the identity of the objects. Hence, equality comparison of instances with the same identity results in equality, and equality comparison of instances with different identities results in inequality. A motivation for this default behavior is the desire that all objects should be reflexive (i.e. x is y implies x == y).

(等価比較(==!=)のデフォルトの動作は、オブジェクトの同一性に基づいています。したがって、同じ同一性を持つインスタンスの等価比較は等しいという結果になり、異なる同一性を持つインスタンスの等価比較は等しくないという結果になります。このデフォルトの動作の動機は、すべてのオブジェクトが反射的であること(つまり、x is yが真ならばx == yも真であること)を望むことです。)

==演算子:オブジェクトの"等価性"を比較する (__eq__())

一方、==演算子は、2つのオブジェクトが値として等しいかどうかを判定します。これは、オブジェクトの__eq__()という特殊メソッドを呼び出すことで実現されています。

# == は値が等しいかを見ている
a = [1, 2, 3]
c = [1, 2, 3]

print(f"a == c: {a == c}")  # aとcは値が等しいのでTrue

実行結果:

a == c: True

重要なのは、この__eq__()メソッドは、クラスを定義する際に開発者が自由にオーバーライド(上書き)できるという点です。

なぜx == Noneは危険なのか?

Noneは、Pythonの実行環境において常に唯一の存在(シングルトン)であることが保証されています。どの変数にNoneを代入しても、それはすべて同じNoneオブジェクトを指します。

a = None
b = None

# aとbは同じNoneオブジェクトを指している
print(f"a is b: {a is b}")       # -> True
print(f"id(a) == id(b): {id(a) == id(b)}") # -> True

この性質から、Noneであるかどうかを判定するには、オブジェクトがNoneそのものであるかを確かめる「同一性」の比較、つまりis演算子が最も確実で適切です。

もし==を使ってしまうと、__eq__()メソッドの予期せぬ実装によって、バグの原因となる可能性があります。例えば、以下のように__eq__をオーバーライドしたクラスを考えてみましょう。

class MyObject:
    def __eq__(self, other):
        # どんな比較対しても常にTrueを返してしまう
        return True

obj = MyObject()

# objはNoneではないのに、== で比較するとTrueになってしまう!
if obj == None:
    print("objはNoneです。 (== で判定)")
else:
    print("objはNoneではありません。 (== で判定)")

# is で判定すれば、正しく判定できる
if obj is None:
    print("objはNoneです。 (is で判定)")
else:
    print("objはNoneではありません。 (is で判定)")

実行結果:

objはNoneです。 (== で判定)
objはNoneではありません。 (is で判定)

このような実装は極端ですが、==は開発者がその振る舞いを変更できるため、Noneという特別な値の判定に使うには不向きなのです。このルールは、Pythonの公式スタイルガイドであるPEP 8でも明確に推奨されています。

Comparisons to singletons like None should always be done with is or is not, never the equality operators.

Noneのようなシングルトンとの比較は、常に is または is not で行うべきであり、等価演算子(==)は決して使うべきではありません。)

if x is None: vs. if not x:

Noneの判定に==ではなく、isを使うのが正しい、ということは分かりました。

一方で、Pythonでは、if not x:のようにオブジェクトが持つ「真偽値」を利用して、より広い意味での「存在しない」「空である」状態を判定することもよくあります。これには、Noneだけでなく、数値の0や空文字列、空リストなども含まれます。

if not x: のような「暗黙的な偽」の利用

Pythonでは、None以外にも「偽(Falsy)」として扱われるオブジェクトがあります。

  • None
  • False
  • 数値のゼロ (0, 0.0, 0jなど)
  • 空のシーケンス ("", [], ())
  • 空のマッピング ({})
  • その他、__bool__()__len__()Falseまたは0を返すオブジェクト
    (詳しくは公式ドキュメントを参照)

関数の戻り値が「値が存在しない」ことを示すためにNoneを返す場合や、リストが空である場合など、「存在しない、または空である」という状態をまとめて判定したいケースは非常に多いです。
このような文脈では、if x is None:と書くよりも、if not x:と書くこともできます。

Pythonにおいて、暗黙的な偽の利用は、PEP 8でも推奨されており、以下のように説明されています。

For sequences, (strings, lists, tuples), use the fact that empty sequences are false:

(シーケンス(文字列、リスト、タプル)では、空のシーケンスが偽であることを利用してください。)

# Correct:
if not seq:
if seq:
# Wrong:
if len(seq):
if not len(seq):

さらに、GoogleのPythonスタイルガイドでも推奨されています。

Use the “implicit” false if possible, e.g., if foo: rather than if foo != []:. There are a few caveats that you should keep in mind though:

  • Always use if foo is None: (or is not None) to check for a None value. E.g., when testing whether a variable or argument that defaults to None was set to some other value. The other value might be a value that’s false in a boolean context!
  • Never compare a boolean variable to False using ==. Use if not x: instead. If you need to distinguish False from None then chain the expressions, such as if not x and x is not None:.
  • For sequences (strings, lists, tuples), use the fact that empty sequences are false, so if seq: and if not seq: are preferable to if len(seq): and if not len(seq): respectively.
  • When handling integers, implicit false may involve more risk than benefit (i.e., accidentally handling None as 0). You may compare a value which is known to be an integer (and is not the result of len()) against the integer 0.

(可能であれば、「暗黙的な」false条件を使用することをお勧めします。例えば、if foo != []: の代わりに if foo: と記述する場合などです。ただし、以下の注意点があります:

  • None値の有無を確認する際は、常に if foo is None: または if foo is not None: を使用してください。例えば、デフォルト値が None に設定されている変数や引数が、他の値に変更されているかどうかをテストする場合などです。なお、他の値にはブール値として false と評価されるものが含まれる可能性がある点にご注意ください!
  • ブール型変数を False と比較する場合、== 演算子は使用しないでください。代わりに if not x: を使用してください。FalseNone を区別する必要がある場合は、if not x and x is not None: のように条件を連鎖させて記述します。
  • シーケンス型(文字列、リスト、タプル)の場合、空のシーケンスは false と評価される性質を利用するため、if seq:if not seq: といった記述方法が、if len(seq):if not len(seq): よりも推奨されます。
  • 整数を扱う場合、暗黙的な false 条件には利点よりもリスクが伴う可能性があります(例えば、誤って None を 0 として扱ってしまうケースなど)。整数であることが確実な値(len() の結果ではない値)を、整数 0 と直接比較する方法も検討してください。)

ただし、予期せぬ入力や結果を招きかねないため、注意が必要です。例えば文字列の"0"Trueになるが、数値の0Falseになるといったような落とし穴があります。

if x is None:if not x: の使い分け

基本的には、if x is None:を使うのが最も明確で安全な方法です。

Noneと他のFalsyな値(0や空文字列など)とで処理が変わるようなケースでは、必ずif x is None:を使うべきです。例えば、以下のような、関数の引数が省略された(デフォルト値のNoneのまま)かどうかを判定するようなケースです。

def my_function(value=None):
    # 引数valueが明確に0や空文字として渡された場合と、
    # 省略された(Noneのまま)場合とで処理を分けたい

    if value is None:
        print("valueは指定されませんでした。")
    elif not value:
        print(f"valueは偽(Falsy)の値({value!r})です。")
    else:
        print(f"valueは真(Truthy)の値({value!r})です。")

my_function()          # 引数を省略
my_function(0)           # 偽(Falsy)だがNoneではない
my_function("")          # 偽(Falsy)だがNoneではない
my_function("hello")     # 真(Truthy)

実行結果:

valueは指定されませんでした。
valueは偽(Falsy)の値(0)です。
valueは偽(Falsy)の値('')です。
valueは真(Truthy)の値('hello')です。

しかし、None以外の値(例えば0や空文字列"")も含めて「存在しない」「空である」としてまとめて扱いたい場合は、if not x:を使うと、よりシンプルなコードになります。

使い分けのまとめ

  • if x is None::まず、0や空文字など他のFalsyな値とは明確に区別したい場合もしくはNoneの判定をしていることを明示的に示したい場合に使う書き方。
  • if not x:: 値が存在しない、または空である状態(None, 0, "", []など)をまとめて扱いたい場合に使う書き方。

余談:is演算子の思わぬ落とし穴

ここまでis演算子の有用性を説明してきましたが、None以外のオブジェクトに使うと思わぬ挙動をすることがあり、注意が必要です。特に、数値や文字列の比較にisを使うのは避けるべきです。

CPython(標準のPython実装)では、内部的な最適化のために、-5から256までの整数オブジェクトをあらかじめキャッシュして使い回しています。(Optimization in Python – Interning)

これにより、この範囲内の整数ではisによる比較がTrueになります。

a = 256
b = 256
print(f"a = {a}, b = {b}")
print(f"a is b: {a is b}")        # -> True
print(f"id(a) == id(b): {id(a) == id(b)}") # -> True

実行結果:

a = 256, b = 256
a is b: True
id(a) == id(b): True

しかし、この範囲外の数値では、同じ値でも異なるオブジェクトとして生成される可能性があります。

x = 257
y = 257
print(f"x = {x}, y = {y}")
print(f"x is y: {x is y}")        # -> False (環境によるが、一般的にFalse)
print(f"id(x) == id(y): {id(x) == id(y)}") # -> False
print(f"x == y: {x == y}")        # -> True (値の比較なので、もちろんTrue)

実行結果:

x = 257, y = 257
x is y: False
id(x) == id(y): False
x == y: True

この挙動はあくまでCPythonの実装の詳細であり、Pythonの言語仕様として保証されているものではありません。したがって、値が同じかどうかを比較したい場合は、必ず==を使いましょう。

より豊富な説明がPython 比較演算子 'is' を使ったときの意外な落とし穴 - Qiitaにもありますので、興味のある方はぜひご覧ください。

まとめ

本記事の要点をまとめます。

  1. Noneの判定にはisを使う

    • isはオブジェクトの同一性id()が同じか)を比較します。
    • Noneはシングルトンオブジェクトなので、同一性で比較するのが最も確実・高速です。
  2. ==でのNone判定は避ける

    • ==はオブジェクトの等価性__eq__()メソッドの結果)を比較します。
    • __eq__()はユーザーが振る舞いを変更できるため、意図しない結果を招く危険性があります。
  3. 文脈に応じてif not x:も活用

    • 「値が存在しない、または空である」ことを広く判定したい場合は、if not x:と書くのがよりシンプルでPythonicです。
    • 0や空文字とNoneを厳密に区別したい場合はif x is None:を使いましょう。
  4. isの多用は禁物

    • isは同一性の比較です。数値や文字列など、一般的なオブジェクトの値の比較には==を使いましょう

これらの原則を理解し、適切に使い分けることで、より良いPythonコードを書くことができます。

参考資料

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?