Python >= 3.3 内部字符串表示

Alb*_* O. 8 python string cpython pep

我正在研究 PEP 393 之后 Python 如何表示字符串,但我不明白 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;
Run Code Online (Sandbox Code Playgroud)

如果我错了,请纠正我,但我的理解是 PyASCIIObject 仅用于包含 ASCII 字符的字符串,PyCompactUnicodeObject 使用 PyASCIIObject 结构,它用于至少包含一个非 ASCII 字符的字符串,而 PyUnicodeObject 用于遗留函数。那是对的吗?

另外,为什么 PyASCIIObject 使用 wchar_t?字符不足以表示 ASCII 字符串吗?另外,如果 PyASCIIObject 已经有了 wchar_t 指针,为什么 PyCompactUnicodeObject 也有一个 char 指针呢?我的理解是两个指针都指向同一个位置,但是为什么要同时包含这两个指针?

Blc*_*ght 6

PEP 373确实是您问题的最佳参考,尽管有时也需要C-API 文档。让我们一一解答您的问题:

  1. 你有正确的类型。但是有一个不明显的问题:当您使用任一“紧凑”类型(PyASCIIObjectPyCompactUnicodeObject)时,结构本身只是一个标题。字符串的实际数据立即存储在内存中的结构之后。数据使用的编码由kind字段描述,并将取决于字符串中的最大字符值。

  2. 前两个结构中的wstrutf8指针是在 C 代码请求时可以存储转换后的表示的地方。对于 ASCII 字符串(使用PyASCIIObject),UTF-8 数据不需要缓存指针,因为 ASCII 数据本身与 UTF-8 兼容。宽字符缓存仅由不推荐使用的函数使用。

    这两个缓存指针永远不会指向同一个地方,因为它们的类型不直接兼容。对于紧凑字符串,它们仅在需要 UTF-8 缓冲区(例如PyUnicode_AsUTF8AndSize)或Py_UNICODE缓冲区(例如已弃用PyUnicode_AS_UNICODE)的函数被调用时才分配。

    对于使用已弃用Py_UNICODE的 API创建的字符串,wstr指针有额外的用途。它指向字符串数据的唯一版本,直到在字符串PyUnicode_READY上调用宏。第一次准备好字符串时,data将创建一个新缓冲区,并将字符存储在其中,使用可能是 Latin-1、UTF-16 和 UTF-32 中最紧凑的编码。该wstr缓冲区将被保留,因为它可能被希望查找一个其他过时的API函数,以后需要PY_UNICODE的字符串。

有趣的是,您现在正在询问 CPython 的内部字符串表示,因为目前正在讨论是否wchar *可以在即将发布的 Python 版本中删除已弃用的字符串 API 函数和实现细节(如指针)。Python 3.11.0(预计将于 2022 年发布)看起来可能会发生这种情况,尽管在那之前计划仍可能发生变化,特别是如果对在野外使用的代码的影响比预期的更严重的话。