Ответ 1
Здесь есть два момента.
Во-первых, Python действительно создает новый символ с вызовом __getitem__
, но только если этот символ имеет порядковое значение больше 256.
Например:
>>> s = chr(256)
>>> s[0] is s
True
>>> t = chr(257)
>>> t[0] is t
False
Это происходит из-за того, что скомпилированная функция getitem
проверяет порядковое значение одиночного chracter и вызывает get_latin1_char
, если это значение равно 256 или меньше. Это позволяет использовать односимвольные строки. В противном случае создается новый объект unicode.
Вторая проблема касается сбора мусора и показывает, что интерпретатор может очень быстро использовать адреса памяти. Когда вы пишете:
>>> s = t # = chr(257)
>>> t[0] is s[0]
False
Сначала Python создает две новые строки с одним символом, а затем сравнивает их адреса памяти. Они имеют разные адреса (у нас есть разные объекты в соответствии с приведенным выше объяснением), поэтому сравнение объектов с is
возвращает False.
С другой стороны, мы можем иметь кажущуюся парадоксальную ситуацию:
>>> id(t[0]) == id(s[0])
True
Но это связано с тем, что интерпретатор быстро повторно использует адрес памяти t[0]
, когда он создает новую строку s[0]
в более поздний момент времени.
Если вы изучите байт-код, который производит эта строка (например, с помощью dis
- см. ниже), вы увидите, что адреса для каждой стороны распределены один за другим (создается новый строковый объект, а затем вызывается id
в теме).
Ссылки на объект t[0]
возвращаются к нулю, как только возвращается id(t[0])
(сейчас мы делаем сравнение по целым числам, а не по самому объекту). Это означает, что s[0]
может повторно использовать один и тот же адрес памяти, когда он будет создан впоследствии.
Вот разобранный байт-код для строки id(t[0]) == id(s[0])
, которую я аннотировал.
Вы можете видеть, что время жизни t[0]
заканчивается до создания s[0]
(ссылки на него отсутствуют), поэтому его память может быть повторно использована.
2 0 LOAD_GLOBAL 0 (id)
3 LOAD_GLOBAL 1 (t)
6 LOAD_CONST 1 (0)
9 BINARY_SUBSCR # t[0] is created
10 CALL_FUNCTION 1 # id(t[0]) is computed...
# ...lifetime of string t[0] over
13 LOAD_GLOBAL 0 (id)
16 LOAD_GLOBAL 2 (s)
19 LOAD_CONST 1 (0)
22 BINARY_SUBSCR # s[0] is created...
# ...free to reuse t[0] memory
23 CALL_FUNCTION 1 # id(s[0]) is computed
26 COMPARE_OP 2 (==) # the two ids are compared
29 RETURN_VALUE