Почему размер этого изменения в коде на Python строку в случае неудачного преобразования инт


С Твитнуть здесь:

import sys
x = 'ñ'
print(sys.getsizeof(x))
int(x) #throws an error
print(sys.getsizeof(x))

мы получаем 74, затем 77 байт для двух getsizeof звонки.

похоже, что мы добавляем 3 байта к объекту из неудачного вызова int.

еще несколько примеров из twitter (возможно, Вам потребуется перезапустить python, чтобы сбросить размер до 74):

x = 'ñ'
y = 'ñ'
int(x)
print(sys.getsizeof(y))

77!

print(sys.getsizeof('ñ'))
int('ñ')
print(sys.getsizeof('ñ'))

74, затем 77.

1 70

1 ответ:

код, который преобразует строки в ints в CPython 3.6 запрашивает UTF-8 форму строки для работы с:

buffer = PyUnicode_AsUTF8AndSize(asciidig, &buflen);

и строка создает представление UTF-8 при первом запросе и кэширует его на строковом объекте:

if (PyUnicode_UTF8(unicode) == NULL) {
    assert(!PyUnicode_IS_COMPACT_ASCII(unicode));
    bytes = _PyUnicode_AsUTF8String(unicode, NULL);
    if (bytes == NULL)
        return NULL;
    _PyUnicode_UTF8(unicode) = PyObject_MALLOC(PyBytes_GET_SIZE(bytes) + 1);
    if (_PyUnicode_UTF8(unicode) == NULL) {
        PyErr_NoMemory();
        Py_DECREF(bytes);
        return NULL;
    }
    _PyUnicode_UTF8_LENGTH(unicode) = PyBytes_GET_SIZE(bytes);
    memcpy(_PyUnicode_UTF8(unicode),
              PyBytes_AS_STRING(bytes),
              _PyUnicode_UTF8_LENGTH(unicode) + 1);
    Py_DECREF(bytes);
}

дополнительные 3 байта предназначены для представления UTF-8.


вам может быть интересно, почему размер не меняется, когда строка что-то вроде '40' или 'plain ascii text'. Это потому, что если строка находится в представление"compact ascii", Python не создает отдельное представление UTF-8. Это возвращает представление ASCII напрямую, который уже действителен UTF-8:

#define PyUnicode_UTF8(op)                              \
    (assert(_PyUnicode_CHECK(op)),                      \
     assert(PyUnicode_IS_READY(op)),                    \
     PyUnicode_IS_COMPACT_ASCII(op) ?                   \
         ((char*)((PyASCIIObject*)(op) + 1)) :          \
         _PyUnicode_UTF8(op))

вы также можете задаться вопросом, почему размер не меняется для чего-то вроде '1'. Это U+FF11 FULLWIDTH Цифра Один, которая int трактует как эквивалент '1'. Это потому что один из более ранних действия в процессе string-to-int это

asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);

который преобразует все пробелы в ' ' и преобразует все десятичные цифры Юникода в соответствующие цифры ASCII. Это преобразование возвращает исходную строку, если она ничего не меняет, но когда она вносит изменения, она создает новую строку, и новая строка-это та, которая получает созданное представление UTF-8.


что касается случаев, когда звонят int на одной струне похоже, это влияет на другой, на самом деле это тот же объект string. Есть много условий, при которых Python будет повторно использовать строки, все так же прочно в странных деталях реализации, как и все, что мы обсуждали до сих пор. Ибо 'ñ', повторное использование происходит потому, что это односимвольная строка в диапазоне Latin-1 ('\x00' -'\xff'), а реализация хранит и повторно использует те.