Why does Python's is operator behave unexpectedly with integers?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Python's is operator checks whether two variables point to the same object in memory — not whether they have the same value. With small integers (typically -5 through 256), CPython caches and reuses the same objects, so is returns True. With larger integers created at runtime, Python allocates separate objects, so is returns False even when the values are equal. This is an implementation detail of CPython's integer interning optimization, and understanding it prevents subtle identity-vs-equality bugs.
is vs ==
== compares values. is compares identity (memory address). For integers, always use == unless you specifically need identity checks.
CPython Integer Interning
CPython pre-allocates integer objects for the range -5 to 256 at startup. Every variable assigned a value in this range points to the same pre-existing object:
The range -5 to 256 was chosen because these values appear frequently in typical programs (loop counters, small constants, return codes).
Compile-Time Constant Folding
Within a single compilation unit (module, function, or interactive statement), CPython may reuse the same object for identical integer literals even outside the interned range:
This is a compiler optimization, not a language guarantee. Different Python implementations (PyPy, Jython, IronPython) may behave differently.
String and Other Object Interning
The same identity gotcha applies to strings and other immutable types:
is is the correct operator for None, True, and False because they are guaranteed singletons. For everything else, use ==.
When to Use is
Common Pitfalls
- Using
isto compare integers or strings:ischecks identity, not equality.a is 257may beTrueorFalsedepending on how the integer was created. Always use==for value comparison. - Relying on integer interning across implementations: The -5 to 256 caching range is a CPython implementation detail. PyPy, Jython, and other implementations may intern different ranges or no range at all. Code that depends on
isfor integers is non-portable. - Assuming compile-time folding is consistent: Within a single function,
a = 1000; b = 1000; a is bmay beTruebecause the compiler reuses the constant. The same code split across REPL lines returnsFalse. Never rely on this behavior. - Forgetting that
isis correct forNone: PEP 8 recommendsif x is Nonerather thanif x == NonebecauseNoneis a singleton and==can be overridden by custom__eq__methods. - Confusing
id()equality withis:id(a) == id(b)is not always equivalent toa is bfor short-lived objects, because Python may reuse memory addresses after garbage collection. Useisdirectly for identity checks.
Summary
ischecks object identity (same memory address);==checks value equality- CPython interns integers from -5 to 256 —
isreturnsTruefor these values - Outside this range, separate integer objects are created and
isreturnsFalse - Compile-time constant folding may cause
isto returnTruewithin a single function even for large integers - Always use
==for value comparison; reserveisforNone, sentinels, and singleton checks - Integer interning is a CPython implementation detail — do not write code that depends on it

