is と == の違い(小さい整数キャッシュで「たまたま動く」)
isはオブジェクトの同一性、==は値の比較です。-5〜256の整数はキャッシュされ、isでもたまたま正しく動いてしまいます。
なぜエラーが出ないのか
出力: False
(エラーなし)- Pythonは何も報告しません。文法として正しいためです
出力: False- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 a = int("1000") 2 b = int("1000") 3 print(a is b) ^
isはオブジェクトの同一性、==は値の比較です。-5〜256の整数はキャッシュされていますが、それ以外の値は計算のたびに別オブジェクトになります。
直し方: is を == にします。
パターン2
1 price1 = int("300") 2 price2 = int("300") 3 print(price1 is price2) ^
256を超える値ではキャッシュが効かず、isは別オブジェクトとしてFalseを返します。小さい値でのテストだけでは気づけません。
直し方: is を == にします。
パターン3
1 x = int("-10") 2 y = int("-10") 3 print(x is y) ^
キャッシュ範囲は-5からです。それより小さい負の値でも同じくisはFalseになります。値の比較には常に==を使います。
直し方: is を == にします。
パターン4
1 a = int("1000") 2 b = int("1000") 3 print(a is b) ^
文字列から変換した整数も、256を超えていればキャッシュされず別オブジェクトになります。値が同じでも同一のオブジェクトとは限りません。
直し方: is を == にします。
パターン5
1 total1 = int("300") + int("0") 2 total2 = int("300") + int("0") 3 print(total1 is total2) ^
別々に計算した合計値も同様です。実行時に計算された値は、同じ結果でも新しいオブジェクトとして作られます。
直し方: is を == にします。
よくある誤解
小さい数値でテストして「isでも動く」と学習してしまうと、大きい数値で突然Falseになり原因が分からなくなります。テストデータの範囲次第で発覚が遅れる典型例です。
実務での勘所
この整数キャッシュはPython言語の仕様ではなく、CPython(標準的な処理系)固有の実装の都合です。PyPyのような別の処理系では、キャッシュの範囲が違ったり、そもそも同じ最適化をしていなかったりします。つまりこの「たまたま動く」挙動は、実行環境を変えただけで再現しなくなる可能性すらあります。実務での鉄則は、値を比較したいなら常に==を使い、isはNoneや特定のシングルトンオブジェクトかどうかを確認する場面だけに限定することです。