Why 100 is 100 is True, but 300 is 300 is False in Python
π― The Questionβ
"In Python's interactive terminal, type
a = 100; b = 100; a is bReturnsTrue. Then typea = 300; b = 300; a is bReturnsFalse! Why does Python behave differently for 100 and 300, and what is the difference betweenisand==?"
β‘ 30-Second Elevator Pitchβ
The discrepancy occurs because of CPython's Small Integer Caching (Integer Interning):
- In Python, everything is an object on the heap, including numbers. Allocating a brand new 28-byte
PyLongObjectfor every single loop counter and index calculation would be catastrophically slow. - During interpreter startup, CPython pre-allocates an internal array of singleton integer objects for the range:
- Whenever you assign a variable to a number in this range (like
100), Python does not create a new objectβit points your variable directly to the pre-existing singleton in memory. - For numbers outside this range (like
300), Python allocates a new, distinct heap object every time. - The
isOperator Trap:==checks Value Equality (a.__eq__(b): Does 300 equal 300?True).ischecks Memory Address Identity (id(a) == id(b): Are they the exact same physical heap object?False).
π§ Under-the-Hood: CPython Small Integer Singleton Arrayβ
π¬ Why Does the Range Start at -5 and End at 256?β
Guido van Rossum and the Python core team profiled millions of lines of real-world Python code:
- Negative numbers smaller than
-5are rarely used. - The numbers
-5through256cover:- Common return codes (
-1,0,1) - Loop iterators and indices
- All 256 ASCII / byte values ()
- Common return codes (
- Caching this array takes negligible memory (~7 KB), but speeds up execution and avoids millions of heap allocations.
π Comparison Matrix: == (Equality) vs. is (Identity)β
| Aspect | == (Value Equality) | is (Reference / Identity Equality) |
|---|---|---|
| What it Compares | Underlying data values | Physical memory addresses (id(a) == id(b)) |
| Underlying Method | Calls a.__eq__(b) | Pointer comparison in C: a == b |
| Can Be Overridden? | β
Yes (Custom classes override __eq__) | β No (Hardcoded silicon pointer comparison) |
| Correct Use Case | Comparing numbers, strings, lists | Checking singletons only (val is None, val is True) |
300 == 300 | β
True | β False |
π‘ What Interviewers Ask Next (Follow-Up Traps)β
-
"Why does
a = 300; b = 300; a is breturnTruewhen run inside a.pyfile or script?"- Answer: This is the Code Block Optimization Trap! In the interactive REPL, each line is compiled independently. But when Python compiles an entire script or function at once, the Constant Folding Compiler notices identical immutable literals in the same code block and reuses the exact same constant object from the code object's
co_conststuple.
- Answer: This is the Code Block Optimization Trap! In the interactive REPL, each line is compiled independently. But when Python compiles an entire script or function at once, the Constant Folding Compiler notices identical immutable literals in the same code block and reuses the exact same constant object from the code object's
-
"Does Python intern strings as well?"
- Answer: Yes! Python automatically interns strings that look like Python identifiers (alphanumeric characters and underscores). You can also manually force string interning in performance-critical code using
sys.intern(string)to enable blazing-fast pointer comparison.
- Answer: Yes! Python automatically interns strings that look like Python identifiers (alphanumeric characters and underscores). You can also manually force string interning in performance-critical code using
Interview Answer: a is b checks memory address identity, not value equality. CPython pre-allocates small integers from to as shared singletons in memory, meaning variables assigned to 100 point to the same object. Numbers outside this range (like 300) are allocated as separate heap objects with different addresses. Always use == for numbers and reserve is for None.