Вопрос-Ответ

Python string interning

Интернирование строк Python

Хотя этот вопрос не имеет реального применения на практике, мне любопытно, как Python выполняет интернирование строк. Я заметил следующее.

>>> "string" is "string"
True

Все так, как я и ожидал.

Вы также можете это сделать.

>>> "strin"+"g" is "string"
True

И это довольно умно!

Но вы не можете этого сделать.

>>> s1 = "strin"
>>> s2 = "string"
>>> s1+"g" is s2
False

Почему бы Python не оценить s1+"g" и не понять, что это то же самое, что s2 и указать на тот же адрес? Что на самом деле происходит в этом последнем блоке, чтобы он вернулFalse?

Переведено автоматически
Ответ 1

Это зависит от конкретной реализации, но ваш интерпретатор, вероятно, интернирует константы времени компиляции, но не результаты выражений во время выполнения.

В дальнейшем используется CPython 3.9.0+.

Во втором примере выражение "strin"+"g" вычисляется во время компиляции и заменяется на "string". Это приводит к тому, что первые два примера ведут себя одинаково.

Если мы изучим байт-коды, то увидим, что они в точности совпадают:

  # s1 = "string"
1 0 LOAD_CONST 0 ('string')
2 STORE_NAME 0 (s1)

# s2 = "strin" + "g"
2 4 LOAD_CONST 0 ('string')
6 STORE_NAME 1 (s2)

Этот байт-код был получен с помощью (который выводит еще несколько строк после приведенного выше):

import dis

source = 's1 = "string"\ns2 = "strin" + "g"'
code = compile(source, '', 'exec')
print(dis.dis(code))

Третий пример включает конкатенацию во время выполнения, результат которой не интернируется автоматически:

  # s3a = "strin"
3 8 LOAD_CONST 1 ('strin')
10 STORE_NAME 2 (s3a)

# s3 = s3a + "g"
4 12 LOAD_NAME 2 (s3a)
14 LOAD_CONST 2 ('g')
16 BINARY_ADD
18 STORE_NAME 3 (s3)
20 LOAD_CONST 3 (None)
22 RETURN_VALUE

Этот байт-код был получен с помощью (который выводит еще несколько строк перед приведенным выше, и эти строки точно такие же, как в первом блоке байт-кодов, приведенном выше):

import dis

source = (
's1 = "string"\n'
's2 = "strin" + "g"\n'
's3a = "strin"\n'
's3 = s3a + "g"')
code = compile(source, '', 'exec')
print(dis.dis(code))

Если бы вы сделали это вручную sys.intern() результат третьего выражения, вы бы получили тот же объект, что и раньше:

>>> import sys
>>> s3a = "strin"
>>> s3 = s3a + "g"
>>> s3 is "string"
False
>>> sys.intern(s3) is "string"
True

Кроме того, Python 3.9 выводит предупреждение для последних двух приведенных выше инструкций:


SyntaxWarning: "is" с литералом. Вы имели в виду "=="?


Ответ 2

Пример 1

>>> x = "123"  
>>> y = "123"
>>> x == y
True
>>> x is y
True
>>> id(x)
50986112
>>> id(y)
50986112

Пример 2

>>> x = "12"
>>> y = "123"
>>> x = x + "3"
>>> x is y
False
>>> x == y
True

Теперь ваш вопрос заключается в том, почему идентификатор одинаков в случае 1, а не в случае 2.

В случае 1 вы назначили строковый литерал "123" для x и y.

Поскольку string неизменяемы, интерпретатору имеет смысл сохранить строковый литерал только один раз и указать все переменные на один и тот же объект.
Следовательно, вы видите идентификатор как идентичный.

В случае 2 вы изменяете x с помощью конкатенации. Оба x и y имеют одинаковые значения, но не одинаковые идентификаторы.
Оба указывают на разные объекты в памяти. Следовательно, они возвращают разные операторы id и is False

python string