Note: all of this code was run on my machine using Python 3.6.1. Not everything will work the same if you test using Python 2.
>>> a = 42 >>> b = 42 >>> a is b True >>> a = 316 >>> b = 316 >>> a is b False
That is suprising! It turns out that all “small integers” with the same value point to the same memory. We can use the Python built-in function
id which returns a value you can think of as a memory address to investigate.
>>> a = 128 >>> b = 256 >>> id(a) 4504844960 >>> id(b) 4504849056 >>> (id(a) - id(b)) / (a - b) 32.0
It looks like there is a table of tiny integers and each integer takes up 32 bytes.
>>> x = 1000 >>> y = 2000 >>> id(x) 4508143344 >>> id(y) 4508143312 >>> id(x) - id(y) 32
It looks like integers that aren’t in the small integers table also take up 32 bytes. The
id for these is way larger than for the small integers which means they are stored somewhere else.
>>> id(x) - id(256) 3294288
What happens if we change the value of an integer in this table? Python has a module called ctypes that can be misused to directly edit memory. (We could also use a debugger but this way all the examples are in Python.)
Note: this code is very platform dependent. If it doesn’t work you might be using Python 2 instead of Python 3. In Python 2 changing
mutate_int so that both instances of 24 are 16 may work.
>>> import ctypes >>> >>> def mutate_int(an_int, new_value): ... ctypes.memmove(id(an_int) + 24, id(new_value) + 24, 8) ... >>> a_number = 7 >>> another_number = 7 >>> mutate_int(a_number, 13) >>> a_number 13 >>> another_number 13
Not only have we changed
another_number but all new references to
>>> for i in range(0, 10): ... print(i) ... 0 1 2 3 4 5 6 13 8 9
Even doing math with
7 no longer works correctly 🎉
>>> 7 13 >>> 6 + 1 13 >>> 7 + 1 14 >>> (7 + 1) - 1 13 >>> 7 * 2 26 >>> 0b1111 ^ 0b1000 13
P.S. You can read more about the table of small integers in the CPython source code.