CPython small-integer cache and why it must not be relied on
CPython preallocates one int object for each value from -5 to 256 at startup and reuses those objects everywhere, because small ints are extremely common and allocating and freeing them constantly would be wasteful. So 256 always resolves to the same object and is returns True, while 257 is created as a fresh object each time it is computed, so two separate creations give two different objects. This is a CPython implementation detail, not language semantics: PyPy, other runtimes and even other CPython versions can behave differently.
The subtlety that trips up good candidates: even 257 can show True. The compiler stores literals in a code object's constants, and identical constants in the same compilation unit are merged. So a = 257; b = 257 on one line, or inside a single script or function, usually prints True, while typing the two assignments on separate lines in the REPL (each compiled separately) prints False. Constant folding matters too: 2**8 + 1 is folded at compile time. To demonstrate the cache without compile-time effects, build the ints at run time.
Version-dependent notes: since 3.12 small ints and some interned strings are 'immortal' (PEP 683), meaning their reference counts are never modified, which helps copy-on-write sharing after fork and is also needed for free-threaded builds. The cached range is an implementation constant, not a documented guarantee. Strings have an analogous but separate mechanism: identifier-like literals are interned automatically and sys.intern lets you intern others explicitly.
The practical takeaway: never use is to compare numbers or strings. Use ==. Valid uses of this knowledge are interview explanations, memory optimization via explicit interning of repeated strings, and recognising flaky tests that compare with is. Python 3.8+ warns on code like x is 200 with a SyntaxWarning for precisely this reason.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience