Check out the following Python code:

>>> x = 256
>>> x is 256
True
>>> y = 257
>>> y is 257
False

WAT! Why is Python doing this?

The answer is twofold:

  • is checks whether 2 things are the same object, not if they’re equal
  • Python caches integers in the range [-5, 256]

is() operator

The is operator tests whether two things point to the same object not if they are equal. To check equality we should use ==.

If we use integers outside of the range [-5, 256], we can see that they can be equal but refer to different objects:

>>> a = 257
>>> b = 257
>>> a is b
False
>>> a == b
True

Integer Caching

So why treat integers from [-5, 256] different from other integers? The answer is efficiency. As explained in the official docs:

“The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object.”

So small integers in the range [-5, 256] are actually stored in the same object! We can prove this by using the built-in id function to return the “identity” of an object, eg. its actual address in memory.

>>> c = 1
>>> d = 1
>>> id(c)
4399848112
>>> id(d)
4399848112

Note: Your actual address in memory will differ from mine, but the address will be the same for both integers, which in Python are just objects.

Takeaways

Understanding how Python works under the hood–its actual implementation in C–is fascinating. And since Python has fantastic documentation, you can find not just the official documentation and source code, but also the discussion of why things are they way they are.

For example, the behavior of Long Integers and Integers in Python 3 was actually proposed back in 2001! And for further reading, there was a major change from Python 2 to Python 3 regarding the treatment of integers which also impacted division on a profound level. Read more here.