What's the difference between `is` and `==`?
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
Nonechecks: alwaysx is None, neverx == None.Noneis a singleton, andisavoids 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 "explicitlyNone". - Identity-sensitive logic: for example, 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.
== 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 ==.