Кеширование небольших чисел в CPython

Автор: CoolPython

Поговорим о небольших числах в CPython. Возьмем, например, два числа, 10 и 300:

>>> a = 10
>>> b = 300
>>> a is 10
True
>>> b is 300
False

Что?! Почему a указывает на свое значение, а b — нет?

Все дело в том, что интерпретатор хранит числа в диапазоне от — 5 до 256 в специальном массиве. Поэтому когда мы пишем x = 10, то вообще-то получаем ссылку на объект, который уже существует в памяти:

>>> c = 1
>>> id(1)
94492411548416
>>> id(c)
94492411548416

Причем это касается любых значений, которые приводятся к целым числам этого диапазона. Можно завести число хоть в двоичном виде, хоть в hex, кеширование все равно будет работать:

>>> id(19)
94579033949504
>>> id(0b10011)
94579033949504
>>> id(0o23)
94579033949504
>>> id(0x13)
94579033949504

Числа вне диапазона — 5, 256 интерпретатор тоже будет пытаться оптимизировать, но только если они находятся в одной строке (или одном файле). В командной строке интерпретатора так не работает:

>>> d = 400
>>> d is 400
False

а так будет:

>>> e = 500; f = 500 
>>> e is f 
True

В общем и целом то, что нужно запомнить — это что небольшие числа в интерпретаторе хранятся не так, как большие, и что с ними следует пользоваться оператором ==, а не is.

И это снова не фича, а деталь реализации для экономии ресурсов. А пишу я об этом, потому что на задачу с кешем наткнулась на собеседовании, и собеседующий удивился, что я в курсе. Сказал, что узнал об этой особенности, когда начал сам проводить собеседования. Теперь это знаете и вы.