Python> = 3.3 Представление внутренней строки - PullRequest
8 голосов
/ 16 июня 2020

Я изучал, как Python представляет строку после PEP 393, и я не понимаю разницы между PyASCIIObject и PyCompactUnicodeObject.

Я понимаю, что строки представлены следующими структурами:

typedef struct {
    PyObject_HEAD
    Py_ssize_t length;          /* Number of code points in the string */
    Py_hash_t hash;             /* Hash value; -1 if not set */
    struct {
        unsigned int interned:2;
        unsigned int kind:3;
        unsigned int compact:1;
        unsigned int ascii:1;
        unsigned int ready:1;
        unsigned int :24;
    } state;
    wchar_t *wstr;              /* wchar_t representation (null-terminated) */
} PyASCIIObject;

typedef struct {
    PyASCIIObject _base;
    Py_ssize_t utf8_length;
    char *utf8;
    Py_ssize_t wstr_length;
} PyCompactUnicodeObject;

typedef struct {
    PyCompactUnicodeObject _base;
    union {
        void *any;
        Py_UCS1 *latin1;
        Py_UCS2 *ucs2;
        Py_UCS4 *ucs4;
    } data;                 
} PyUnicodeObject;

Поправьте меня, если я ошибаюсь, но я понимаю, что PyASCIIObject используется только для строк с символами ASCII, PyCompactUnicodeObject использует структуру PyASCIIObject и используется для строк с хотя бы одним символом, отличным от ASCII, а PyUnicodeObject - это используется для устаревших функций. Это правильно?

Кроме того, почему PyASCIIObject использует wchar_t? Разве символа недостаточно для представления строк ASCII? Кроме того, если PyASCIIObject уже имеет указатель wchar_t, почему PyCompactUnicodeObject также имеет указатель char? Насколько я понимаю, оба указателя указывают на одно и то же место, но зачем вам включать оба?

1 Ответ

6 голосов
/ 18 июня 2020

PEP 373 действительно лучший справочник по вашим вопросам, хотя C -API docs иногда также необходимы. Давайте рассмотрим ваши вопросы один за другим:

  1. Вы правильно поняли типы. Но есть одна неочевидная загвоздка: когда вы используете какой-либо из «компактных» типов (PyASCIIObject или PyCompactUnicodeObject), сама структура представляет собой просто заголовок. Фактические данные строки сохраняются сразу после структуры в памяти. Кодировка, используемая данными, описывается полем kind и будет зависеть от наибольшего символьного значения в строке.

  2. Указатели wstr и utf8 в первые две структуры - это места, где преобразованное представление может быть сохранено, если оно запрашивается кодом C. Для строки ASCII (с использованием PyASCIIObject) указатель кеша для данных UTF-8 не требуется, поскольку сами данные ASCII совместимы с UTF-8. Кэш широких символов используется только устаревшими функциями.

    Два указателя кеша никогда не будут указывать на одно и то же место, поскольку их типы напрямую не совместимы. Для компактных строк они выделяются только тогда, когда функции требуется буфер UTF-8 (например, PyUnicode_AsUTF8AndSize) или Py_UNICODE буфер (например, устаревший PyUnicode_AS_UNICODE) вызывается.

    Для строк, созданных с помощью устаревших API на основе Py_UNICODE, указатель wstr имеет дополнительное применение. Он указывает на только версию строковых данных до тех пор, пока для строки не будет вызван макрос PyUnicode_READY. При первой загрузке строки будет создан новый буфер data, и символы будут сохранены в нем с использованием наиболее компактной кодировки из возможных среди Latin-1, UTF-16 и UTF-32. Буфер wstr будет сохранен, так как он может понадобиться позже другим устаревшим функциям API, которые захотят найти строку PY_UNICODE.

Интересно, что вы спрашивать о внутренних строковых представлениях CPython прямо сейчас, так как обсуждается в настоящее время о том, могут ли устаревшие строковые функции API и детали реализации, такие как указатель wchar *, быть удалены в следующей версии из Python. Похоже, это может произойти для Python 3.11.0 (который, как ожидается, будет выпущен в 2022 году), хотя планы могут измениться до этого, особенно если влияние на код, используемый в дикой природе, будет более серьезным, чем ожидалось.

...