Как 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