Интернирование строк 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