Pythonで if x == None: と書いてはいけない理由を徹底的に説明する


## はじめに

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

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

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

  * なぜ`None`の判定に`==`ではなく`is`を使うべきなのか、その根本的な理由
  * `if x is None:`よりもPythonic（Pythonらしい）とされる書き方とは何か
  * `is`演算子を使う上での思わぬ落とし穴

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

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

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

そして、**`None`はPythonの実行環境全体でただ一つしか存在しない「シングルトン（Singleton）」オブジェクト**であるため、同一性での比較が最も確実なのです。
実際、[公式ドキュメント](https://docs.python.org/3/reference/datamodel.html#none)では次のように説明されています。
> 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)`を実行しているのと等価です。

```python
# 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
```

ちなみに、[公式ドキュメント](https://docs.python.org/3/reference/expressions.html#value-comparisons)では、`is`演算子は次のようにも説明されています。つまり、デフォルト動作では、`x is y`が`True`ならば`x == y`も`True`になるということです。
> 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__()`という特殊メソッドを呼び出すことで実現されています。

```python
# == は値が等しいかを見ている
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`オブジェクトを指します。

```python
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__`をオーバーライドしたクラスを考えてみましょう。

```python
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](https://peps.python.org/pep-0008/#programming-recommendations)でも明確に推奨されています。

> 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:`と書けば常にOKか？

`None`の判定に`is`を使うのが正しい、ということは分かりました。では、`None`かどうかをチェックしたい場合は、常に`if x is None:`と書くのが最善なのでしょうか？

実は、よりPythonic（Pythonらしい）とされる書き方が存在する場合があります。それは、**オブジェクトが持つ「真偽値」を利用する方法**です。

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

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

  * `None`
  * `False`
  * 数値のゼロ (`0`, `0.0`, `0j`など)
  * 空のシーケンス (`""`, `[]`, `()`)
  * 空のマッピング (`{}`)
  * その他、`__bool__()`や`__len__()`が`False`または`0`を返すオブジェクト
（詳しくは[公式ドキュメント](https://docs.python.org/3/library/stdtypes.html#truth-value-testing)を参照）

関数の戻り値が「値が存在しない」ことを示すために`None`を返す場合や、リストが空である場合など、「存在しない、または空である」という状態をまとめて判定したいケースは非常に多いです。

このような文脈では、`if x is None:`と書くよりも、`if not x:`と書く方がシンプルでPythonicだとされています。

これは、[PEP 8](https://peps.python.org/pep-0008/#programming-recommendations)でも推奨されており、以下のように説明されています。
> For sequences, (strings, lists, tuples), use the fact that empty sequences are false:
>
> （シーケンス（文字列、リスト、タプル）では、空のシーケンスが偽であることを利用してください。）
> ```python
> # Correct:
> if not seq:
> if seq:
> ```
> ```python
> # Wrong:
> if len(seq):
> if not len(seq):
> ```


さらに、[GoogleのPythonスタイルガイド](https://google.github.io/styleguide/pyguide.html#2144-decision)でも推奨されている書き方です。

> Use the “implicit” false if possible, e.g., `if foo:` rather than `if foo != []:`.
>
> （可能であれば「暗黙的な」偽を使用してください。例：`if foo != []:` ではなく `if foo:`）

### `if x is None:` と `if not x:` の使い分け

もちろん、**`0`や空文字(`""`)と`None`を明確に区別したい場合**は、`if x is None:`（または`if x is not None:`）を使う必要があります。

例えば、関数の引数が省略された（デフォルト値の`None`のまま）かどうかを判定するようなケースです。

```python
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'）です。
```

**使い分けのまとめ**

  * **`if not x:`**: 値が存在しない、または空である状態（`None`, `0`, `""`, `[]`など）をまとめて扱いたい場合に使う、よりPythonicな書き方。
  * **`if x is None:`**: `0`や空文字など他のFalsyな値とは明確に区別して、「`None`であること」自体を厳密に判定したい場合に使う。

## 余談：`is`演算子の思わぬ落とし穴

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

CPython（標準のPython実装）では、内部的な最適化のために、**-5から256までの整数オブジェクトをあらかじめキャッシュして使い回しています**。([Optimization in Python – Interning](https://towardsdatascience.com/optimization-in-python-interning-805be5e9fd3e/))

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

```python
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
```

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

```python
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](https://qiita.com/realyutanemoto/items/919c10d28d6b4f83de97)にもありますので、興味のある方はぜひご覧ください。

## まとめ

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

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コードを書くことができます。

## 参考資料
  * [Python 3 documentation](https://docs.python.org/3/reference/)
  * [PEP 8 – Style Guide for Python Code](https://peps.python.org/pep-0008/)
  * [styleguide | Style guides for Google-originated open-source projects](https://google.github.io/styleguide/pyguide.html)