How Python Caches Small Integers Between -5 and 256
In Python, numbers are not primitive data types but fully allocated objects that consume memory and processing time during creation. To optimize performance and reduce memory usage, the standard Python implementation (CPython) pre-allocates and caches all integer objects between -5 and 256 during interpreter initialization. This article breaks down how this small integer caching mechanism works under the hood, why this specific range exists, and how it directly affects identity checks in your code.
The Underlying Mechanism in CPython
In CPython, every integer is an instance of
PyLongObject. Allocating and deallocating memory each time
a number is referenced would create significant overhead. To resolve
this, CPython defines two internal macros:
NSMALLNEGINTS(set to 5)NSMALLPOSINTS(set to 257)
During the interpreter's startup routine, CPython creates a static
array containing pre-allocated integer objects covering the range from
-NSMALLNEGINTS to NSMALLPOSINTS - 1,
corresponding to the range [-5, 256].
Whenever an integer is created in your code—whether through
assignment, a function return value, or an arithmetic operation—CPython
checks if the resulting value falls within this inclusive range. If it
does, Python does not allocate a new PyLongObject. Instead,
it increments the reference count of the existing cached object in the
array and returns a pointer to it.
Why the -5 to 256 Range Was Chosen
The range of -5 to 256 is an empirical design choice based on typical programming patterns:
- Common Usage: Integers such as 0, 1, and 2 are ubiquitous as loop counters, collection indices, boolean states, and default flags.
- Negative Offsets: Numbers between -5 and -1
frequently serve as return codes (such as
-1denoting an error or absence in a search) or negative indices. - Byte Values: The upper limit of 256 encompasses all possible values of an 8-bit unsigned byte (0 through 255), which are common in binary, networking, and string operations.
Extending the cache to a much larger range would waste system memory by keeping unused objects permanently loaded, while a smaller range would lose the performance gains achieved by avoiding repeated memory allocation.
Identity (is) vs.
Equality (==)
Because cached integers are singletons in memory, this mechanism
directly influences the behavior of the identity operator
(is), which checks whether two variables point to the exact
same memory address.
# Within the cached range
a = 100
b = 100
print(a is b) # True (both point to the same cached object)
print(a == b) # True (values are equal)
# Outside the cached range (in the interactive REPL)
x = 300
y = 300
print(x is y) # False (distinct objects allocated in memory)
print(x == y) # True (values are equal)Note: Running code from a script file or single compilation block
may show x is y as True for numbers outside
this range due to a separate optimization called code-block constant
folding. However, the small integer cache is global, permanent, and
independent of compiler optimizations.
Key Takeaways
- Automatic Initialization: CPython allocates an array of 262 integer objects (-5 to 256) at startup.
- Pointer Reuse: Any variable holding a value within this boundary references the pre-existing object rather than instantiating a new one.
- Always Use
==for Values: Never rely onisto compare numerical values, as relying on object identity will fail as soon as an integer exceeds the boundaries of the cache.