As mentioned by glglgl, this is an implementation detail of CPython. If you look at Objects/longobject.c
in the source code for CPython (e.g. version 3.3.0), you'll find the answer to what's happening:
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* Small integers are preallocated in this array so that they
can be shared.
The integers that are preallocated are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
This explains why, after a = 1; b = 1
, a is b
will be True
, even when you say a += 2; b +=2; a -= 2; b -= 2
. Whenever a number is calculated to have a value that fits in this array, the resulting object is simply picked from this array instead, saving a bit of memory.
You can figure out the bounds of this small_ints
array using a function like this:
def binary_search(predicate, lo, hi):
while lo + 1 < hi:
mid = (lo + hi) / 2
if predicate(mid):
lo = mid
else:
hi = mid
return lo
def is_small_int(n):
p = n + 1
q = n + 1
return (p - 1) is (q - 1)
def min_neg_small_int():
p, q = -1, -1
if p is not q:
return 0
while p is q:
p += p
q += q
return binary_search(is_small_int, p / 2, p) - 1
def max_pos_small_int():
p, q = 1, 1
if p is not q:
return 0
while p is q:
p += p
q += q
return binary_search(is_small_int, p / 2, p)
def small_int_bounds():
return (min_neg_small_int(), max_pos_small_int())
For my build (Python 2.7, 64-bit Windows build), small_int_bounds() == (-5, 256)
. This means that numbers between -5
and 256
(inclusive) are shared through the small_ints
array in Objects/longobject.c
.
-edit- I see elssar noted that there is a similar answer about interning of some literals. This fact is also mentioned in the documentation for PyInt_FromLong
, as mentioned by this answer.