Почему в Python отдельные значения строки словаря проходят проверку на равенство? ( строка Интернирующий эксперимент )

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

import sys

top = 10000

non1 = []
non2 = []
for i in range(top):
    s1 = '{:010d}'.format(i)
    s2 = '{:010d}'.format(i)
    non1.append(s1)
    non2.append(s2)

same = True
for i in range(top):
    same = same and (non1[i] is non2[i])
print("non: ", same) # prints False
del non1[:]
del non2[:]


with1 = []
with2 = []
for i in range(top):
    s1 = sys.intern('{:010d}'.format(i))
    s2 = sys.intern('{:010d}'.format(i))
    with1.append(s1)
    with2.append(s2)

same = True
for i in range(top):
    same = same and (with1[i] is with2[i])
print("with: ", same) # prints True

###############################

non_dict = {}
non_dict[1] = "this is a long string"
non_dict[2] = "this is another long string"
non_dict[3] = "this is a long string"
non_dict[4] = "this is another long string"

with_dict = {}
with_dict[1] = sys.intern("this is a long string")
with_dict[2] = sys.intern("this is another long string")
with_dict[3] = sys.intern("this is a long string")
with_dict[4] = sys.intern("this is another long string")

print("non: ",  non_dict[1] is non_dict[3] and non_dict[2] is non_dict[4]) # prints True ???
print("with: ", with_dict[1] is with_dict[3] and with_dict[2] is with_dict[4]) # prints True

Я думал, что проверки без диктовки приведут к «ложной» распечатке, но я явно ошибался. Кто-нибудь знает, что происходит, и принесет ли интернирование строк какие-либо преимущества в моем случае? У меня может быть много, много ключей, чем одно значение, если я объединяю данные из нескольких входных текстов, поэтому я ищу способ сэкономить место в памяти. (Возможно, мне придется использовать базу данных, но это выходит за рамки этого вопроса.) Заранее спасибо!


person synchronizer    schedule 01.01.2017    source источник
comment
Что сказал 2357112. Обратите внимание, что сконструированные строки обычно не будут повторно использовать интернированное значение, например, a="a long string";b="a long" + " string";print(id(a)==id(b)) печатает False   -  person PM 2Ring    schedule 01.01.2017


Ответы (1)


Одна из оптимизаций, выполняемых компилятором байт-кода, похожая на интернирование, но отличная от него, заключается в том, что он будет использовать один и тот же объект для одинаковых констант в одном и том же блоке кода. Строковые литералы здесь:

non_dict = {}
non_dict[1] = "this is a long string"
non_dict[2] = "this is another long string"
non_dict[3] = "this is a long string"
non_dict[4] = "this is another long string"

находятся в одном и том же блоке кода, поэтому одинаковые строки в конечном итоге представляются одним и тем же строковым объектом.

person user2357112 supports Monica    schedule 01.01.2017
comment
Ах, это правильно! Я только что попробовал это, и введение изменчивости во время выполнения приводит к ожидаемой распечатке False. Спасибо за разъяснения. u_in = input(введите строку времени выполнения: ) non_dict = {} non_dict[1] = это длинная строка + u_in non_dict[2] = это другая длинная строка + u_in non_dict[3] = это длинная строка + u_in non_dict[4] = это еще одна длинная строка + u_in - person synchronizer; 01.01.2017