Как Python определяет, идентичны ли две строки
Я попытался понять, когда строки Python идентичны (они также используют одно и то же расположение памяти). Однако во время моих тестов, похоже, нет очевидного объяснения, когда две строковые переменные, которые равны, используют одну и ту же память:
import sys
print(sys.version) # 3.4.3
# Example 1
s1 = "Hello"
s2 = "Hello"
print(id(s1) == id(s2)) # True
# Example 2
s1 = "Hello" * 3
s2 = "Hello" * 3
print(id(s1) == id(s2)) # True
# Example 3
i = 3
s1 = "Hello" * i
s2 = "Hello" * i
print(id(s1) == id(s2)) # False
# Example 4
s1 = "HelloHelloHelloHelloHello"
s2 = "HelloHelloHelloHelloHello"
print(id(s1) == id(s2)) # True
# Example 5
s1 = "Hello" * 5
s2 = "Hello" * 5
print(id(s1) == id(s2)) # False
Строки неизменяемы, и, насколько я знаю, Python пытается повторно использовать существующие неизменяемые объекты, имея в виду, что другие переменные указывают на них вместо создания новых объектов в памяти с тем же значением.
Имея это в виду, кажется очевидным, что Example 1
возвращает True
.
По-прежнему очевидно (мне), что Example 2
возвращает True
.
Мне не кажется очевидным, что Example 3
возвращает False
- я не делаю то же, что и в Example 2
?!?
Я наткнулся на этот вопрос SO:
Почему сравнение строк в Python с использованием "==" или "is" иногда приводит к другому результату?
и прочитайте http://guilload.com/python-string-interning/ (хотя я, вероятно, все это не понял), и yougt - хорошо, может быть, "интернированные" строки зависят по длине, поэтому я использовал HelloHelloHelloHelloHello
в Example 4
. Результат был True
.
И то, что меня озадачило, делало то же самое, что и в Example 2
, только с большим числом (но оно фактически возвращало бы ту же строку, что и Example 4
) - однако на этот раз результат был False
?!?
Я действительно не знаю, как Python решает, использовать ли тот же объект памяти или когда создать новый.
Являются ли какие-либо официальные источники, которые могут объяснить это поведение?
Ответы
Ответ 1
От ссылка, которую вы отправили:
Избежать больших файлов .pyc
Итак, почему 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
не оценивается до True
? Вы помните файлы .pyc, с которыми вы сталкиваетесь во всех своих пакетах? Ну, байт-код Python хранится в этих файлах. Что произойдет, если кто-нибудь напишет что-то вроде этого ['foo!'] * 10**9
? Полученный файл .pyc будет огромным! Чтобы избежать этих явлений, последовательности, генерируемые посредством оптимизации глазок, отбрасываются, если их длина превосходит 20.
Если у вас есть строка "HelloHelloHelloHelloHello"
, Python обязательно будет хранить ее как есть (попросить интерпретатора обнаружить повторяющиеся шаблоны в строке для экономии места, может быть, слишком много). Однако, когда дело доходит до строковых значений, которые могут быть вычислены во время разбора, например, "Hello" * 5
, Python оценивает их как часть этой так называемой "оптимизации глазок", которая может решить, стоит ли или нет прекомпретировать строка. Поскольку len("Hello" * 5) > 20
, интерпретатор оставляет его так, чтобы избежать слишком большого количества длинных строк.
EDIT:
Как указано в этом вопросе, вы можете проверить это на исходном коде в peephole.c
, функция fold_binops_on_constants
, ближе к концу вы увидите:
// ...
} else if (size > 20) {
Py_DECREF(newconst);
return -1;
}
ИЗМЕНИТЬ 2:
На самом деле, этот код оптимизации недавно был перемещен в оптимизатор AST, так что теперь вам нужно будет заглянуть в ast_opt.c
, функция fold_binop
, которая вызывает функцию safe_multiply
, которая проверяет, что строка не больше MAX_STR_SIZE
, недавно определено как 4096. Таким образом, кажется, что предел был значительно усилен для следующих выпусков.
Ответ 2
В примере 2:
# Example 2
s1 = "Hello" * 3
s2 = "Hello" * 3
print(id(s1) == id(s2)) # True
Здесь значение s1 и s2 оценивается во время компиляции. Это вернет true.
В примере 3:
# Example 3
i = 3
s1 = "Hello" * i
s2 = "Hello" * i
print(id(s1) == id(s2)) # False
Здесь значения s1 и s2 оцениваются во время выполнения, и результат не интерналируется автоматически, поэтому возвращается false. Это делается для того, чтобы избежать избыточного выделения памяти, создав строку HelloHelloHello во время выполнения.
Если вы вручную сделаете intern, он вернет True
i = 3
s1 = "Hello" * i
s2 = "Hello" * i
print(id(intern(s1)) == id(intern(s2))) # True