sodcalculator

10 Tech How-To

Why Python’s ‘is’ Works for Some Integers but Fails for Others

Tag 10-27513 · Updated · Standard formula, shown below

The calculator on this page is switched off while it is checked. The formula is below, and the worked example shows how to do the sum by hand.

Python’s is operator compares object identity (whether two names point to the exact same object in memory), while == compares values. For small integers, Python caches and reuses a single object, so is often returns True; for larger integers, each occurrence creates a new object, so is returns False even when the values match.

Formula

The behavior isn’t a formula but a rule of CPython’s implementation. The expression a is b evaluates to True if and only if id(a) == id(b) — that is, both variables reference the same memory address. The expression a == b evaluates to True if the integer values are numerically equal. The caching rule is: integers from -5 to 256 are pre-created as singletons at interpreter startup, so any reference to that value points to the same object. Outside that range, each literal or computed result creates a new object unless the interpreter optimizes it away.

Worked Example

Using the defaults from the calculator (a = 256, b = 256):

  1. Both variables are assigned the integer 256.
  2. Because 256 is within the cached range (-5 to 256), both names point to the same pre-existing object.
  3. a is b returns True (1 in the calculator).
  4. a == b returns True (1 in the calculator).

If you change the inputs to 257 and 257, the calculator shows same = 0 (False) because 257 is outside the cache, so each literal creates a new object. Yet eq remains 1 because the values are equal.

How Integer Caching Works

CPython, the standard Python interpreter, pre-allocates a fixed array of integer objects for values from -5 to 256. This is an implementation detail, not part of the Python language specification. The range was chosen because small integers appear frequently in loops, indexing, and arithmetic, so reusing objects saves memory and speeds up comparisons. When you write x = 100, Python looks up the cached object for 100 and points x to it. When you write x = 300, Python allocates a new integer object on the heap, and y = 300 allocates another distinct object. Thus x is y is False even though x == y is True.

When the Rule Seems to Break

You might see is return True for large integers in interactive sessions or within functions. This happens because of a compile-time optimization called constant folding: if the same literal appears in the same code block, the compiler may reuse the same object for both. For example, in a single line like a = 1000; b = 1000, CPython sometimes stores both as the same constant, so a is b is True. But if you compute one value (e.g., a = 1000; b = 999 + 1), the result is a new object, so is returns False. This inconsistency confuses many programmers and is a strong reason to avoid is for value comparisons.

How to Compare Integers Safely

Always use == or != when you want to compare integer values. Use is only when you explicitly need to check object identity, such as comparing to None (where None is a singleton) or checking if two variables reference the same mutable object. For integers, the only time is is reliable is when you are certain both values are within the cached range and you are not relying on any optimization. In practice, that means never — because code changes can shift values outside the range. The Python documentation itself warns: “The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. Object identity is determined using the id() function.” It does not guarantee any caching behavior.

Common Mistakes and Misconceptions

  • Assuming is compares values. This is the root of the confusion. is is about identity, not equality.
  • Thinking the cache range is universal. The -5 to 256 range is CPython-specific. Other Python implementations (PyPy, Jython, IronPython) may have different caching rules or none at all.
  • Expecting consistent results across code blocks. Constant folding can make is return True for large literals in one context and False in another, depending on how the code is compiled.
  • Using is for integers in performance-critical code. Even if it works in your current environment, it can break when you move to a different interpreter or Python version.

Performance and Memory Implications

Integer caching is a memory-saving optimization. Without it, every loop iteration that creates a small integer (e.g., for i in range(100)) would allocate a new object, causing massive overhead. By reusing objects, CPython keeps memory usage low and speeds up arithmetic and comparisons. For large integers, the cost of allocation is unavoidable because the number of possible values is infinite. This is a trade-off: the interpreter cannot pre-cache every integer, so it caches only the most common ones. Understanding this helps explain why is behaves differently across ranges — it’s not a bug, but a deliberate design choice.

Alternatives to is for Identity Checks

If you need to check whether two variables refer to the same object (for any type), use is — that’s its purpose. For integers, however, you rarely need identity. If you suspect two variables might be different objects, use id() to inspect their memory addresses, but remember that id() is also implementation-specific. For value comparison, the only correct operator is ==. In code reviews, many teams enforce a rule: “Use is only for None, True, False, and singleton objects.” This avoids the entire class of bugs.

Frequently Asked Questions

Why does is return True for 256 but False for 257?

Because CPython caches integers from -5 to 256. When you assign 256 to two variables, both point to the same cached object. 257 is not cached, so each assignment creates a new object, and is returns False.

Is the -5 to 256 range guaranteed in all Python versions?

No. It’s an implementation detail of CPython. The language specification does not require any caching. Other implementations or future versions could change it, so you should never rely on it.

Can I use is to compare large integers if I’m careful?

Technically, you can, but it’s fragile. Constant folding may make it work for literals in the same code block, but any computation or function call can create a new object. The safe practice is to always use == for value comparison.

Does this affect floats or strings?

Yes, similar caching exists for small strings and interned strings, but the rules are different and even less predictable. Floats are not cached, so is almost always returns False for separate float literals. Always use == for numeric and string values.

Why does Python cache integers at all?

To save memory and improve performance. Small integers are used constantly in loops and arithmetic; reusing objects avoids the overhead of allocating and deallocating millions of objects during a program’s lifetime.

What should I do if I see is used for integers in existing code?

Replace it with == unless the code explicitly relies on identity (which is almost never the case for integers). This makes the code correct across all Python implementations and versions.

Related

Formula used
in a | First integer | - | 256 | -1000000 | 1000000
in b | Second integer | - | 256 | -1000000 | 1000000
out same | Same object? (is) | - | 1 if a is b else 0 | 0
out eq | Same value? (==) | - | 1 if a == b else 0 | 0

Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More in this bed

The whole Tech How-To bed →