Поведение Python: id() в интерпретаторе

Я столкнулся с этим странным поведением, которое происходит только в интерактивном сеансе Python, но не тогда, когда я пишу script и выполняю его.

String - неизменяемый тип данных в Python, следовательно:

>>> s2='string'
>>> s1='string'
>>> s1 is s2
True

Теперь, странная часть:

>>> s1='a string'
>>> s2='a string'
>>> s1 is s2
False

Я видел, что наличие пробела в строке вызывает такое поведение. Если я поместил это в script и запустил его, в обоих случаях результат будет True.

Кто-нибудь знает об этом? Спасибо.

EDIT:

Хорошо, вышеупомянутые вопросы и ответы дают некоторые идеи. Теперь вот еще один эксперимент:

>>> s2='astringbstring'
>>> s1='astringbstring'
>>> s1 is s2
True

В этом случае строки определенно длиннее, чем 'a string', но все еще имеют одинаковые идентификаторы.

Ответ 1

Большое спасибо @eryksun за исправления!

Это связано с вызовом механизма interning в Python:

Введите строку в таблицу "интернированных" строк и верните интернированная строка - это сама строка или копия. Внутренние струны полезно получить небольшую производительность при поиске в словаре - если ключи в словаре интернированы, а ключ поиска интернирован, ключевые сравнения (после хэширования) могут быть сделаны с помощью сравнения указателя вместо сравнения строк. Обычно имена, используемые в Python программы автоматически интернированы, а словари, используемые для хранения атрибуты модуля, класса или экземпляра имеют интернированные ключи.

Изменено в версии 2.3: Интернированные строки не являются бессмертными (например, они раньше был в Python 2.2 и раньше); вы должны держать ссылку на возвращаемое значение intern() вокруг, чтобы извлечь выгоду из него.

CPython автоматически ставит short определенные строки (1 буквенные строки, ключевые слова, строки без пробелов, которые были назначены), чтобы увеличить скорость поиска и скорость сравнения: например, 'dog' is 'dog' будет указателем сравнение вместо полного сравнения строк. Тем не менее, для автоматического интернирования для всех (более длинных) строк требуется гораздо больше памяти, что не всегда возможно, и, следовательно, они могут не иметь одинакового идентификатора, что делает результаты id() разными, например,:

# different id when not assigned
In [146]: id('dog')
Out[146]: 4380547672

In [147]: id('dog')
Out[147]: 4380547552

# if assigned, the strings will be interned (though depends on implementation)
In [148]: a = 'dog'

In [149]: b = 'dog'

In [150]: id(a)
Out[150]: 4380547352

In [151]: id(b)
Out[151]: 4380547352

In [152]: a is b
Out[152]: True

Для целых чисел, по крайней мере, на моей машине, CPython автоматически будет автоматизировать до 256 автоматически:

In [18]: id(256)
Out[18]: 140511109257408

In [19]: id(256)
Out[19]: 140511109257408

In [20]: id(257)
Out[20]: 140511112156576

In [21]: id(257)
Out[21]: 140511110188504

ОБНОВЛЕНИЕ благодаря @eryksun: в этом случае строка 'a string' не интернирована, потому что CPython только ставит строки без пробелов, а не из-за длины, которую я сразу принял: например, буквы ASCII, цифры и подчеркивание.

Для получения дополнительной информации вы также можете обратиться к Alex Martelli здесь.