backend / python core / tricky questions / 03_is_vs_equals_interning.md

is vs == and string/int interning

1 min read source

is vs == and string/int interning

The gotcha

is compares identity (same object in memory). == compares value. CPython caches small ints and short strings, so is sometimes “works” by accident — until it doesn’t.

Minimal repro

a = 256; b = 256
print(a is b)        # True   (cached)

a = 257; b = 257
print(a is b)        # False  (not cached) — but in REPL same line might be True
                     # because the compiler folds constants per code object

s = "hello"; t = "hello"
print(s is t)        # True   (string literals are interned)

s = "hello world!"; t = "hello world!"
print(s is t)        # often False — long/whitespace strings not auto-interned

Why it happens

CPython preallocates small int objects in range [-5, 256] (the “small int cache”) for performance — int(5) always returns the same object. String literals that look like identifiers are interned automatically; runtime-built strings usually aren’t.

These are CPython implementation details. PyPy, Jython, etc. behave differently. Never rely on this.

How to avoid

Use is only for: None, True, False, sentinels, and explicit identity checks (e.g. comparing two objects to see if they’re literally the same).

Use == for everything else — value comparison.

if x is None:        # correct idiom
    ...
if x == None:        # works but linted-out; relies on __eq__
    ...
if value is 5:       # even if it works today, broken in principle
    ...

Interview angle

“What’s the difference between is and ==?” is a warm-up. The follow-up — “why does 1000 is 1000 return False but 100 is 100 returns True?” — separates candidates who memorized vs. understood.