What's the difference between `is` and `==`?

4 minbeginnerfundamentalsidentityequality

Quick Answer

`==` calls `__eq__` and checks **value equality** (do these objects represent the same value?). `is` checks **identity** — whether two names point to the *same object in memory* (equivalent to `id(a) == id(b)`). Use `is` for singletons like `None`, `True`, `False`, and `is not` for sentinel checks; use `==` for comparing values.

Detailed Answer

== vs is

== invokes __eq__ and answers "are these values equal?" is answers "are these literally the same object?" (identical id()).

a = [1, 2, 3]
b = [1, 2, 3]
c = a

a == b   # True  -- same contents
a is b   # False -- two distinct list objects
a is c   # True  -- c is a name bound to the same object as a

Why a == b can be True while a is b is False

Two separate lists (or dicts, or custom objects with a custom __eq__) can be equal in value without being the same object. The default __eq__ inherited from object actually falls back to identity, but built-in containers and most user classes override it to compare contents.

When to use is

  • None checks: always x is None, never x == None. None is a singleton, and is avoids accidentally invoking a custom __eq__ that might behave unexpectedly.
  • Singletons/sentinels: x is True, or a private sentinel object (_MISSING = object()) used to distinguish "not provided" from "explicitly None".
  • Identity-sensitive logic: e.g., checking whether a cache returned the exact cached instance rather than an equal copy.

The small-integer/string trap

CPython caches small integers (-5 to 256) and some string literals, so is can appear to work for equality by coincidence:

x = 256
y = 256
x is y   # True (cached)

x = 257
y = 257
x is y   # False on most builds — a new object, no caching guarantee

This is a CPython implementation detail, not part of the language spec — relying on it for anything beyond None/True/False is a bug waiting to happen.

Interview-ready summary: == compares values via __eq__; is compares identity via id(). Always use is for None/singleton checks and == for everything else — never rely on integer/string caching as a substitute for ==.