Типичная запись из файла выглядит следующим образом:
rows[0]
{'route': '3', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354}
Это означает, что большинство ваших неизменяемых объектов являются строками, и только значение 'rides'
является целым числом.
Для маленьких целых чисел (-5...255
) Python3 сохраняет целочисленный пул - поэтому эти маленькие целые числа кажутся кэшированными (если используются PyLong_FromLong
и Co.).
Правила более сложны для строк - они, как указывает @timgeb, интернированы.Существует отличная статья об интернировании , даже если речь идет о Python2.7 - но с тех пор мало что изменилось.В двух словах, самые важные правила:
- все строки длины
0
и 1
интернированы. - строки с более чем одним символом интернированы, если они состоятсимволов, которые можно использовать в идентификаторах и которые создаются во время компиляции либо напрямую, либо с помощью оптимизация глазка / постоянное свертывание (но во втором случае, только если результат не превышает 20символы ( 4096, начиная с Python 3.7 ).
Все вышеперечисленное является подробностями реализации, но, принимая их во внимание, мы получаем следующее для row[0]
выше:
'route', 'date', 'daytype', 'rides'
все интернированы, потому что они созданы во время компиляции функции read_as_dicts
и не имеют "странных" символов. '3'
и 'W'
интернированы, потому что интернированыих длина составляет всего 1
. 01/01/2001
не интернирована, потому что она длиннее 1
, создана во время выполнения и не может быть квалифицирована в любом случае, поскольку содержит символ /
. 7354
не из маленького целочисленного пула, потому что слишком большой.Но другие записи могут быть из этого пула.
Это было объяснение текущего поведения, только некоторые объекты были "кэшированы".
Но почему Python не кэширует все созданные строки / целые числа?
Начнем с целых чисел.Чтобы иметь возможность быстрого поиска, если целое число уже создано (намного быстрее, чем O(n)
), необходимо сохранить дополнительную структуру данных для поиска, которая требует дополнительной памяти.Однако целых чисел так много, что вероятность повторного попадания в одно уже существующее целое число не очень велика, поэтому накладные расходы памяти для структуры данных look-up-data в большинстве случаев не возвращаются.
Поскольку строкам требуется больше памяти, относительная (оперативная) стоимость структуры данных поиска не так высока.Но нет смысла интернировать 1000-символьную строку, потому что вероятность того, что случайно созданная строка будет иметь те же символы, почти равна 0
!
С другой стороны, если, например, в качестве структуры поиска используется хэш-словарь, вычисление хеша займет O(n)
(n
-количество символов), что, вероятно, выигралоне окупаются за большие строки.
Таким образом, Python делает компромисс, который работает довольно хорошо в большинстве сценариев - но он не может быть идеальным в некоторых особых случаях.Тем не менее, для этих особых сценариев вы можете оптимизировать для каждой руки, используя sys.intern()
.
Примечание: Наличие одного и того же идентификатора не означает, что он является одним и тем же объектом, если время жизнииз двух объектов не перекрываются, - так что ваши рассуждения в вопросе не являются влагозащищенными - но это не имеет значения в этом особом случае.