Интернирование происходит, когда код, т. Е. 'a'*4096
, компилируется.Обычно при компиляции это приводит к следующему байт-коду:
0 LOAD_CONST 1 ('a')
2 LOAD_CONST 2 (4096)
4 BINARY_MULTIPLY
Однако, поскольку оба являются постоянными, постоянное свертывание может быть выполнено для BINARY_MULTIPLY
во время компиляции с помощью peephole-optimizer, что происходит в fold_binop
:
static int
fold_binop(expr_ty node, PyArena *arena, int optimize)
{
...
PyObject *newval;
switch (node->v.BinOp.op) {
...
case Mult:
newval = safe_multiply(lv, rv);
break;
case Div:
...
}
return make_const(node, newval, arena);
}
, если safe_multiply
можно оценить, результат добавляется в список констант в make_const
, если safe_multiply
возвращает NULL
, ничего не происходит -оптимизация не может быть выполнена.
safe_multiply
выполняется, только если результирующая строка не длиннее 4096 символов:
#define MAX_STR_SIZE 4096 /* characters */
static PyObject *
safe_multiply(PyObject *v, PyObject *w)
{
...
else if (PyLong_Check(v) && (PyUnicode_Check(w) || PyBytes_Check(w))) {
Py_ssize_t size = PyUnicode_Check(w) ? PyUnicode_GET_LENGTH(w) :
PyBytes_GET_SIZE(w);
if (size) {
long n = PyLong_AsLong(v);
if (n < 0 || n > MAX_STR_SIZE / size) { //HERE IS THE CHECK!
return NULL;
}
}
}
...
}
Теперь все строковые константыинтернированный, как только объект кода создан в PyCode_New
:
PyCodeObject *
PyCode_New(..., PyObject *consts,...)
{
...
intern_string_constants(consts);
...
И, таким образом, 'a'*4096
становится интернированным, а 'a'*4097
- нет, и для первого случая оптимизированные байтовые кодытеперь:
0 LOAD_CONST 3 ('aaaaaaaaa...aaaaaaaaaaa')
Похоже, что это ответственный коммит и это соответствующая ошибка .
Поведение изменилось со времени Python3.7 - в Python3.6 ограничение по-прежнему составляло 20.
В старая версия , продукт был выполнен и впоследствии проверен, что размер результата меньше 21:
static int
fold_binop(expr_ty node, PyArena *arena)
{
...
PyObject *newval;
switch (node->v.BinOp.op) {
...
case Mult:
newval = PyNumber_Multiply(lv, rv);
break;
...
default: // Unknown operator
return 1;
}
/* Avoid creating large constants. */
Py_ssize_t size = PyObject_Size(newval);
if (size == -1) {
...
}
else if (size > 20) { //HERE IS THE CHECK OF SIZE
Py_DECREF(newval);
return 1;
}
...
Что касается того, почему старое значение было 20
, а текущее значение 4096
, я могу только догадываться.Однако 4096
звучит не так уж и много для юникод-объектов на современных машинах, в то время как 20
был порогом для всех классов, а не только для юникода.