Skip to the solution
MediumDSA14 min read

Why does Python cache small integers?

469 views

The problem

Question presented to candidate: "In CPython, a = 256; b = 256; a is b gives True, but the same thing with numbers built at run time above 256 gives False. Why does Python cache small integers, which ones, and what does that mean for code that uses is on numbers?"

What a strong answer should cover:

  • CPython preallocates one object for every int from -5 to 256 at start-up and returns that shared object whenever such a value is produced, by a literal, by int("7") or by arithmetic
  • The reason: small ints are by far the most common values (indexes, counters, lengths, flags), and an int object is a full heap object (28 bytes on a 64-bit build), so sharing saves allocations, frees and memory; it is safe only because ints are immutable
  • It is an implementation detail of CPython, not a language guarantee; the range is a compile-time constant (_PY_NSMALLNEGINTS = 5, _PY_NSMALLPOSINTS = 257 in the 3.11-3.13 headers)
  • Separate trap: equal constants inside one compiled unit are stored once, so x = 1000; y = 1000; x is y can be True in a script and False typed line by line in the REPL; constant folding (999 + 1) joins in
  • is on numbers is a bug: compare values with ==; CPython 3.8+ even emits SyntaxWarning: "is" with a literal
  • Since 3.12 (PEP 683) small ints, None, True and False are immortal: their reference count is pinned and never changes

Code / implementation expected: Optional β€” showing the boundary with int(str(n)) and explaining why literals mislead is the strong version.

  • int
  • caching
  • interning

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

14 min read Β· 10 sections

Run the code

Edit the code and run it here. Each console line with an expected value becomes a test.

PythonFinding the cached range and what the cache saves
PythonThe is traps: shared constants, the REPL and constant folding
PythonImmortal small ints on CPython 3.12+
import sys

# Run on CPython 3.12+ (64-bit). PEP 683 made small ints immortal:
# their reference count is pinned and never changes.
print(sys.version_info >= (3, 12))

one = int("1")
before = sys.getrefcount(one)
refs = [one] * 1000
after = sys.getrefcount(one)
print("small int 1   :", before == after, before == 2**32 - 1)

big = int("123456")
before = sys.getrefcount(big)
refs = [big] * 1000
after = sys.getrefcount(big)
print("big int 123456:", after - before)

# None, True, False and small ints share the same pinned count
print(sys.getrefcount(None) == sys.getrefcount(True) == 2**32 - 1)

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 88 of 139 in Python

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Python questions
Up nextMedium

What is string interning in Python?

Continue